【问题标题】:How can I know how much memory an STL object takes?我如何知道一个 STL 对象占用了多少内存?
【发布时间】:2011-11-18 21:30:29
【问题描述】:

我需要在我的程序中收集有关内存使用情况的统计信息。

我的代码主要是使用 STL 编写的。

有什么方法可以知道 STL 对象消耗了多少内存?

例如,

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

s1s2 消耗了多少内存? 显然,sizeof(string)+s1.length() 不太准确。

【问题讨论】:

  • 这完全是特定于实现的。 C++ 所说的关于大小的唯一一件事就是 sizeof(char) 是一个。而已。除此之外,您还需要使用特定于您的平台和编译器的工具来测量内存使用情况。
  • 你的意思是不使用分析器(比如 Quantify)?
  • 是的,不使用分析器。我想在程序中放置一些内存使用的“自我诊断”代码。我自己写的课很容易做到。但是很难对 STL 创建的对象了解很多。

标签: c++ memory stl


【解决方案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

【讨论】:

  • 你能发布一个工作代码吗?可能与ideone链接?我试图编译这个,稍作修改(因为它没有编译),但它没有工作:ideone.com/FPI61
  • 它现在可以在 MSVC 和 ideone 上编译。有很多错误。遇到的错误是 mem_used&lt;TrackType&gt;() += n;,其中 mem_used 返回一个 pointer
【解决方案2】:

由于这完全是实现细节,您无法用100% 准确度来确定这一点。

然而,正如你所说,你想在你的程序中放一些代码来统计内存使用情况,那么你可以做到这一点比较准确。

我相信,对于std::string,字符串对象占用的内存大小几乎等于:

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

同样,对于std::vector

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

您可以根据此类信息对您的统计估计进行建模。对于矢量,您还可以考虑T 的大小,其方式与在上述等式中填充.... 的方式相同。例如,如果Tstd::string,那么:

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

如果Tint,那么

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

我希望,这样的分析可以帮助您尽可能准确地计算尺寸。

【讨论】:

  • @Tomalak:呵呵……没问题:D
  • * sizeof(char) 为什么? sizeof(char) 不能是 1 以外的值。
  • @Ildjarn: 避免魔法常数,因此它匹配所有其他“statisticalSize”函数
  • @ildjarn:我知道。但是写出来也没什么坏处。在我看来,它增加了可读性,因为它带来了一致性。
  • @Mooing :我并不是在提倡用1 替换sizeof(char),我想知道为什么首先要执行乘法运算。 Nawaz 对“一致性”的回答对我来说已经足够好了。
【解决方案3】:

只是为了好玩,我尽我最大的努力找出您提供的各个字符串的内存使用情况。我同意有人说这基本上是不可能的。我的实现在很多方面都不好:

  • #define private public 表明我“做错了”,这取决于一些不仅没有标准化的东西,而且即使是我的 STL 实现(gcc 4.6)也可能会在下一个版本中改变。你不应该在生产代码中看到这一点。
  • 我认为您正在寻找可用于任何 STL 对象的东西,而我只实现了std::string。您必须为每种类型执行特定逻辑,因为我的平台上不存在通用机制。对于容器,你必须递归; O(1) get_allocated_size() 没有现有的准确计数器。
  • 我平台的字符串是引用计数的;因此,如果您使用我的函数将一堆字符串的大小相加,您将高估任何彼此共享表示的字符串。
  • 我还用malloc_usable_size查看了malloc返回的区域的实际大小。首先,这是特定于glibc 中包含的分配器。其次,它不计算malloc的记账内存。第三,这种分配可能会导致其他一些分配占用更多内存,因为它在虚拟地址空间或类似空间中引入了碎片。但它比调用者要求的更准确,因为 malloc 往往会四舍五入。

在实践中,我建议进行 Nawaz 提到的那种近似,并像其他人提到的那样,通过全过程测量来凭经验对其进行验证。

话虽如此,我们开始:

$ 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

【讨论】:

  • 根据标准#define private public 将未定义的行为引入您的程序。不要那样做。永远!
  • 我认为我的程序中不乏未定义的行为。这是一个玩具。
【解决方案4】:

我认为不可能测量单个容器。

但是,要获得总体摘要,您可以使用mallinfo() 函数(在 Linux 上)。

如果你想知道你的程序中最大的内存消耗在哪里,你可以在 valgrind 下使用它的massif memory profiler 运行它,它会给你类似“15 MB 是从map&lt;my_data&gt;::... 分配的信息”,所以你可以猜猜你的结构有多大。

【讨论】:

    【解决方案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
    }
    

    【讨论】:

      猜你喜欢
      • 2011-02-03
      • 1970-01-01
      • 2011-09-06
      • 2011-02-21
      • 2022-11-22
      • 2015-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多