简体   繁体   English

我怎么知道STL对象需要多少内存?

[英]How can I know how much memory an STL object takes?

I need to gather statistics about memory usage in my program. 我需要在程序中收集有关内存使用情况的统计信息。

My code is mostly written using STL. 我的代码主要使用STL编写。

Is there any way of learning how much memory is being consumed by an STL object? 有没有办法了解STL对象消耗了多少内存?

For example, 例如,

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

How much memory is consumed by s1 and s2 ? s1s2消耗了多少内存? Obviously, sizeof(string)+s1.length() is not quite accurate. 显然, sizeof(string)+s1.length()不太准确。

If you're willing to be slightly intrusive, you can make a custom allocator to track all heap usage by container type, or by type allocated. 如果您愿意稍微介入一下,可以创建一个自定义分配器来按容器类型或已分配的类型跟踪所有堆使用情况。 This is quite intrusive, but quite accurate as well, for determining memory usage. 这非常具有侵入性,但也非常准确,用于确定内存使用情况。 This does not track how much memory the heap itself takes though, as that is highly OS-dependent. 这不会跟踪堆本身占用多少内存,因为这是高度依赖于操作系统的。

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;
    }
};

And usage is: 用法是:

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 results: Windows结果:

0 //this is zero, because the string did not allocate heap space. 0 //这是零,因为字符串没有分配堆空间。
64 64
11 11

http://ideone.com/lr4I8 (GCC) results: http://ideone.com/lr4I8(GCC )成果:

24 24
92 92
11 11

Since this is completely implementation details, you cannot determine this with 100% accuracy. 由于这是完全实现的细节,因此无法以100%准确度来确定。

However, as you said, you want to put some code to statistics the memory usage in your program, then you can do that with some accuracy. 但是,正如您所说,您希望使用一些代码来统计程序中的内存使用情况 ,然后您可以准确地执行此操作。

I believe, for std::string , the size of memory taken by string objects, will be almost equal to: 我相信,对于std::string ,字符串对象占用的内存大小几乎等于:

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

And similarly, for std::vector 同样,对于std::vector

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

You can model your statistical estimation on such informations. 您可以对此类信息建模统计估算。 For vector, you can also consider the size of T in the same way which will fill the .... in the above equation. 对于矢量,您也可以用相同的方式考虑T的大小,它将填充上述等式中的.... For example, if T is std::string , then: 例如,如果Tstd::string ,那么:

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

And if T is int , then 如果Tint ,那么

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

I hope, such analysis would help you to compute the size with as much accuracy as possible. 我希望,这样的分析可以帮助您尽可能准确地计算尺寸。

Just for fun, I made my best attempt to find the memory usage of the individual strings you gave. 只是为了好玩,我尽最大努力找到你给出的单个字符串的内存使用情况。 I concur with people who say this is basically impossible; 我同意那些说基本上不可能的人; my implementation is not good in many ways: 我的实现在很多方面都不好:

  • #define private public gives away that I'm "doing it wrong" and depending on something which is not only not standardized but even for my STL implementation (gcc 4.6) might change by next release. #define private public表示我“做错了”并且取决于不仅没有标准化的东西,甚至我的STL实现(gcc 4.6)可能会在下一个版本中改变。 You should never see this in production code. 你永远不应该在生产代码中看到这一点。
  • I think you're looking for something usable for any STL object, and I only implemented std::string . 我认为你正在寻找可用于任何STL对象的东西,我只实现了std::string You'd have to do specific logic for each type as no general mechanism exists on my platform. 您必须为每种类型执行特定逻辑,因为我的平台上不存在任何通用机制。 And for containers, you'd have to recurse; 对于容器,你必须递归; there are no existing accurate counters for an O(1) get_allocated_size() . O(1) get_allocated_size()没有现有的精确计数器。
  • My platform's strings are reference-counted; 我的平台的字符串是引用计数的; so if you add up sizes of a bunch of strings with my function you will overestimate any ones which share representations with each other. 因此,如果您使用我的函数添加一堆字符串的大小,您将高估任何彼此共享表示的字符串。
  • I also used malloc_usable_size to see the actual size of the area returned by malloc. 我还使用malloc_usable_size来查看malloc返回的区域的实际大小。 First, this is specific to the allocator included with glibc . 首先,这是glibc附带的分配器特有的。 Second, it doesn't count malloc's book-keeping memory. 其次,它不计算malloc的簿记记忆。 Third, this allocation might cause some other allocation to take up more memory because it is introducing fragmentation in the virtual address space or some such. 第三,这种分配可能会导致其他一些分配占用更多内存,因为它会在虚拟地址空间中引入碎片或其他类型。 But it is more accurate than what the caller asked for as malloc tends to round things up. 但它比调用者要求的更准确,因为malloc倾向于将事情弄清楚。

In practice, I'd suggest make the sort of approximation Nawaz mentioned and validating it empirically with whole-process measurements as other people have mentioned. 在实践中,我建议将Nawaz提及的近似值与其他人提到的全过程测量结果进行经验验证。

With that said, here we go: 话虽如此,我们走了:

$ 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

I don't think it would be possible to measure for an individual container. 我认为不可能测量单个容器。

However, to get an overall summary, you can use the mallinfo() function (on Linux). 但是,要获得总体摘要,可以使用mallinfo()函数(在Linux上)。

If you want to know where are the biggest memory hogs in your program, you can run it under valgrind with its massif memory profiler , which will give you information like "15 MB were allocated from map<my_data>::... ", so you can guess how large your structures are. 如果你想知道你的程序中最大的内存占用量,你可以在valgrind下使用它的massif内存分析器来运行它,它将为你提供“15 MB从map<my_data>::... ”分配的信息,所以你可以猜出你的结构有多大。

You can use the Psapi for this if you want general memory usage statistics. 如果需要常规内存使用情况统计信息,可以使用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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM