Memory consumption by STL containers

17,834

Solution 1

For std::vector and std::string, capacity, rather than size, would be a better approximation. For node based containers (std::set, etc.), you'd want multiply the number of nodes (roughly the number of elements) times the size of each node. This only accurate, however, if the allocator doesn't use an optimized pool allocator for the nodes.

If you really want to know how much memory is being used, however, a better strategy would be to replace the global operator new and operator delete, and keep track of the actual allocations. Even more accurate would be to replace malloc and free. Formally, this is not allowed, but in practice, I've never encountered an implementation where it doesn't work. On the other hand, if you replace malloc and free, you have to implement the actual memory management yourself. If you replace operator new and operator delete, you can use malloc and free, which makes it fairly trivial.

Note too that each allocation has some fixed overhead. A 100000 allocations of 10 bytes each will consume significantly more memory than 10 allocations of 100000 bytes each.

Solution 2

A std::vector<element> typically takes 3 machine words in total + sizeof(element) * capacity() of memory. For typical implementations, the overhead consist of pointers to the beginning, end and current size of the vector. The elements themselves are stored in contiguous memory. capacity() typically has room for up to twice the actual number of elements.

A std::map<element, int> typically takes about 2 machine words in total + 3 machine words per element + [ sizeof(element) +sizeof(int) ] * num_elements of memory. For typical implementations, the overhead consists of pointers to the stored elements. The elements themselves are stored in a binary tree, with pointers to its parent and two children.

With these rules of thumb, all you need to know is the average number of characters per string and the total number of strings to know total memory consumption.

Share:
17,834
Frank Q.
Author by

Frank Q.

Updated on June 04, 2022

Comments

  • Frank Q.
    Frank Q. almost 2 years

    I am working on an application in which I am planning to use couple of STL containers. The application will take certain steps if memory consumption reaches a threshold. For that purpose I need to perform a close to accurate calculation on how much memory is used by STL containers.

    vector<string> StringList
    map<string, int> mapstring
    

    This is how I am estimating memory:

    For size of StringList, loop over all the elements of the vector and keep adding the string sizes.

    string size = sizeof(string) + string.capacity()*sizeof(char)
    

    Then finally add to this sizeof(StringList);

    For size of mapstring, loop over all the keys of the container and keep adding the string sizes and then add the sizes of int which is mapstring.size()*sizeof(int). Then finally add to this sizeof(mapstring);

    I guess a better approach would be specifying own allocator class and keeping track of memory usage inside it but writing one could be non-trivial. Does this estimation look good ?

  • James Kanze
    James Kanze over 11 years
    For std::vector, you should use capacity, not the number of elements. And if std::map uses a pool allocator (and a lot do), then it's likely allocating a lot more nodes than there are elements in the map.
  • TemplateRex
    TemplateRex over 11 years
    @JamesKanze Tnx, updated for capacity() remark. So for std::map, is there a non-intrusive way (i.e. not logging the allocator) to get memory consumption?
  • James Kanze
    James Kanze over 11 years
    If you don't know the implementation, there's no way; if the allocator uses a pool of nodes, there may be no way, period. The simplest solution here is just to replace global operator new and operator delete with ones which keep track of allocations. (The default allocators are required to use ::operator new to acquire the memory they allocate.)
  • James Kanze
    James Kanze over 11 years
    Also, your comment concerning capacity() can have room for up to twice the actual number of elements isn't correct. There's no restriction what so ever on capacity, except that it grow by multiples. Typical multiples are 1.5 and 2, but larger (or smaller) are certainly allowed.
  • TemplateRex
    TemplateRex over 11 years
    @JamesKanze I repeatedly mentiond "typical implementation", as there is no mandated implementation anyway.
  • James Kanze
    James Kanze over 11 years
    Exponential growth is mandatory. I'd guess that 1.5 is the most common factor, with 2 a close second.
  • Frank Q.
    Frank Q. over 11 years
    Replacing new will give me the memory used during heap allocations. To this usage I Will have to add overhead of all containers i.e sizeof(vector) + sizeof(string) etc. ?
  • James Kanze
    James Kanze over 11 years
    It depends on what you are trying to measure. Replacing new will give you use of dynamic memory in C++ (but not in any C parts you might be using). This is what I would understand by "memory consumption". It will not count the three pointers in an std::vector which is a local variable on the stack, but it will count all of the memory used by all of the strings in such a vector (since all of the memory for the contents of a vector is dynamically allocated).