Как узнать, сколько памяти занимает объект STL?

Мне нужно собрать статистику использования памяти в моей программе.

Мой код в основном написан с использованием STL.

Есть ли способ узнать, сколько памяти потребляется объектом STL?

Например,

string s1 = "hello";
string s2 = "hellohellohellohellohellohellohellohellohellohellohellohellohello";

Сколько памяти потребляется s1 и s2? Очевидно, что sizeof(string)+s1.length() не совсем точен.

Ответы

Ответ 1

Если вы хотите немного навязчиво, вы можете сделать собственный распределитель для отслеживания всего использования кучи по типу контейнера или по выделенному типу. Это довольно навязчиво, но довольно точно, для определения использования памяти. Это не отслеживает, сколько памяти занимает куча, хотя это сильно зависит от ОС.

template<class TrackType> 
size_t* mem_used() {static size_t s = 0; return &s;}

template<class T, class TrackType, class BaseAllocator = std::allocator<T> > 
class TrackerAllocator : public BaseAllocator {
public:
    typedef typename BaseAllocator::pointer pointer;
    typedef typename BaseAllocator::size_type size_type;

    TrackerAllocator() throw() : BaseAllocator() {}
    TrackerAllocator(const TrackerAllocator& b) throw() : BaseAllocator(b) {}
    TrackerAllocator(TrackerAllocator&& b) throw() : BaseAllocator(b) {}
    template <class U> TrackerAllocator(const typename TrackerAllocator::template rebind<U>::other& b) throw() : BaseAllocator(b) {}
    ~TrackerAllocator() {}

    template<class U> struct rebind {
        typedef TrackerAllocator<U, TrackType, typename BaseAllocator::template rebind<U>::other> other;
    };

    pointer allocate(size_type n) {
        pointer r = BaseAllocator::allocate(n);
        *mem_used<TrackType>() += n;
        return r;
    }
    pointer allocate(size_type n, pointer h) {
        pointer r = BaseAllocator::allocate(n, h);
        *mem_used<TrackType>() += n;
        return r;
    }
    void deallocate(pointer p, size_type n) throw() {
        BaseAllocator::deallocate(p, n);
        *mem_used<TrackType>() -= n;
    }
};

И используется:

typedef std::basic_string<char, 
                          std::char_traits<char>,
                          TrackerAllocator<char, std::string> > trackstring;
typedef std::vector<int, 
                    TrackerAllocator<int, std::vector<int> > > trackvector;
//                                        ^              ^
//                                        This identifies which memory to track
//                                        it can be any type, related or no.
//                                        All with the same type will be tracked togeather
int main() {
    trackstring mystring1("HELLO WORLD");
    std::cout << *mem_used<std::string>() << '\n'; //display memory usage of all strings

    trackstring mystring2("MUCH LONGER STRING THAT DEFINITELY GETS HEAP ALLOCATED!");
    std::cout << *mem_used<std::string>() << '\n'; //display memory usage of all strings

    trackvector myvec(mystring1.begin(), mystring1.end());
    std::cout << *mem_used<std::vector<int> >() << '\n'; //display memory usage of all vector<int>
    //                     ^              ^
    //                     This identifies which memory type from above to look up.
    return 0;
}

Результаты Windows:

0//это нуль, потому что строка не выделяла кучу пространства.
64
11

http://ideone.com/lr4I8 (GCC):

24
92
11

Ответ 2

Поскольку это полностью детали реализации, вы не можете определить это с точностью 100%.

Однако, как вы сказали, вы хотите поместить некоторый код в статистику использования памяти в своей программе, тогда вы можете сделать это с некоторой точностью.

Я считаю, что для std::string размер памяти, взятой строковыми объектами, будет почти равен:

size_t statisticalSizeStr = sizeof(string)+ s.capacity() * sizeof(char);

Аналогично, для std::vector

size_t statisticalSizeVec = sizeof(std::vector<T>)+ ....;

Вы можете моделировать свою статистическую оценку по такой информации. Для вектора вы также можете рассмотреть размер T таким же образом, который заполнит .... в приведенном выше уравнении. Например, если T - std::string, то:

size_t vecSize = sizeof(std::vector<std::string>);
size_t statisticalSizeVec = vecSize  + v.capacity() * statisticalSizeStr;

И если T есть int, то

size_t statisticalSizeVec=sizeof(std::vector<int>)+v.capacity()*sizeof(int);

Надеюсь, такой анализ поможет вам вычислить размер с максимально возможной точностью.

Ответ 3

Просто для удовольствия я попытался найти использование памяти для отдельных строк, которые вы дали. Я согласен с людьми, которые говорят, что это принципиально невозможно; моя реализация не очень хороша во многих отношениях:

  • #define private public дает, что я "делаю это неправильно" и в зависимости от того, что не только не стандартизировано, но даже для моей реализации STL (gcc 4.6) может измениться к следующей версии. Вы никогда не должны видеть это в производственном коде.
  • Я думаю, что вы ищете что-то, пригодное для использования для любого объекта STL, и я реализовал только std::string. Вам нужно будет делать определенную логику для каждого типа, поскольку на моей платформе не существует общего механизма. И для контейнеров вам придется переписывать; нет существующих точных счетчиков для O(1) get_allocated_size().
  • Мои строки платформы подсчитываются по ссылке; поэтому, если вы добавляете размеры связки строк с моей функцией, вы будете переоценивать любые, которые разделяют представления друг с другом.
  • Я также использовал malloc_usable_size, чтобы увидеть фактический размер области, возвращаемой malloc. Во-первых, это относится к распределителю, включенному в glibc. Во-вторых, он не учитывает память памяти malloc. В-третьих, это распределение может привести к тому, что какое-то другое распределение займет больше памяти, поскольку оно вводит фрагментацию в виртуальном адресном пространстве или некоторых таких. Но он более точен, чем то, о чем звонил собеседник, поскольку malloc имеет тенденцию обходить все.

На практике я бы предложил сделать приближение, на которое Наваз упомянул и подтвердил его эмпирически с измерениями всего процесса, как упомянули другие люди.

С учетом сказанного здесь мы идем:

$ cat > sizetest.cc <<EOF
#define private public  // eww...

#include <malloc.h>

#include <iostream>
#include <string>

using namespace std;

// NON-PORTABLE! Totally dependent on gcc 4.6 / glibc (with the stock allocator)!    
size_t get_size(const string &s) {
  string::_Rep *rep = (string::_Rep*) s.data() - 1;
  return sizeof(string) + malloc_usable_size(rep);
}

int main(int argc, char **argv) {
  string s1 = "hello";
  string s2 = "hellohellohellohellohellohellohellohellohellohellohellohellohello";
  cout << "s1 size: " << get_size(s1) << endl;
  cout << "s2 size: " << get_size(s2) << endl;
  return 0;
}
EOF
$ g++ -Wall sizetest.cc -o sizetest
$ ./sizetest 
s1 size: 48
s2 size: 112

Ответ 4

Я не думаю, что можно было бы измерить для отдельного контейнера.

Однако, чтобы получить общее резюме, вы можете использовать функцию mallinfo() (в Linux).

Если вы хотите узнать, где находятся самые большие в вашей программе памяти, вы можете запустить его под valgrind с помощью массива памяти профилировщика, который предоставит вам информацию, например, "15 МБ были выделены из map<my_data>::...", поэтому вы можете догадаться, насколько велики ваши структуры.

Ответ 5

Вы можете использовать Psapi для этого, если вы хотите использовать общую статистику использования памяти.

    #ifdef WIN32
    #include <psapi.h>
    #elif __GNUC__
        #include <sys/time.h>
        #include <sys/resource.h>
    #endif

void MemoryUsage()
{
#ifdef WIN32
    PROCESS_MEMORY_COUNTERS pmc;
    if(GetProcessMemoryInfo(GetCurrentProcess(),&pmc,sizeof(pmc)))
    {
                // do something with the values you care about
        pmc.PagefileUsage;
        pmc.PeakPagefileUsage;
        pmc.PeakWorkingSetSize;
        pmc.QuotaNonPagedPoolUsage;
        pmc.QuotaPagedPoolUsage;
        pmc.QuotaPeakNonPagedPoolUsage;
        pmc.WorkingSetSize;
        }
#else
    // I'm not a penguin
#endif
}