Как я могу узнать, сколько памяти занимает объект STL? - PullRequest
15 голосов
/ 16 сентября 2011

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

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

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

Например,

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

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

Ответы [ 5 ]

15 голосов
/ 16 сентября 2011

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

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

5 голосов
/ 16 сентября 2011

Поскольку это полностью детали реализации, вы не можете определить это с точностью 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 голосов
/ 16 сентября 2011

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

  • #define private public выдает, что я "делаю это неправильно" и зависит от чего-то, что не только не стандартизировано, но даже для моей реализации STL (gcc4.6) может измениться к следующему выпуску.Вы никогда не должны видеть это в рабочем коде.
  • Я думаю, вы ищете что-то пригодное для любого объекта STL, и я реализовал только std::string.Вы должны будете сделать определенную логику для каждого типа, так как на моей платформе не существует общего механизма.А для контейнеров, вам придется возвращаться;нет точных счетчиков для O(1) get_allocated_size().
  • Строки моей платформы рассчитаны по ссылкам;поэтому, если вы добавите размеры набора строк с помощью моей функции, вы переоцените любые из них, которые имеют общие представления друг с другом.
  • Я также использовал malloc_usable_size, чтобы увидеть фактический размер области, возвращаемой функцией malloc.Во-первых, это относится к распределителю, включенному в glibc.Во-вторых, он не учитывает память Маллока.В-третьих, это распределение может привести к тому, что какое-то другое выделение займет больше памяти, поскольку оно вводит фрагментацию в виртуальное адресное пространство или что-то подобное.Но это более точно, чем то, о чем просил вызывающий объект, поскольку 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
2 голосов
/ 16 сентября 2011

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

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

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

1 голос
/ 16 сентября 2011

Вы можете использовать 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...