【问题标题】:Memory consumption by STL containersSTL 容器的内存消耗
【发布时间】:2012-08-04 04:42:09
【问题描述】:

我正在开发一个应用程序,我计划在其中使用几个 STL 容器。如果内存消耗达到阈值,应用程序将采取某些步骤。为此,我需要对 STL 容器使用多少内存进行接近准确的计算。

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

这是我估计内存的方式:

对于StringList 的大小,循环遍历向量的所有元素并继续添加字符串大小。

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

然后最后添加到这个sizeof(StringList);

对于 mapstring 的大小,循环遍历容器的所有键并继续添加字符串大小,然后添加 int 的大小,即mapstring.size()*sizeof(int)。然后最后添加到这个sizeof(mapstring);

我想一种更好的方法是指定自己的分配器类并跟踪其中的内存使用情况,但编写一个可能并非易事。这个估计好看吗?

【问题讨论】:

  • 类似问题:stackoverflow.com/questions/2160300/… stackoverflow.com/questions/720507/… 这是获得粗略下限的合理方法,但开销可能比您想象的要多得多。例如,std::map 元素必须包含各种额外的指针,以及与保持平衡相关的信息,... Valgrind 工具 massif 跟踪堆分配,并且可能有助于分析您的程序内存使用情况。
  • 它还取决于您使用的 STL 实现。例如STLPort 或 gcc STL
  • “我想一种更好的方法是指定自己的分配器类并跟踪其中的内存使用情况,但编写一个可能并非易事。” -- 这确实是最好的方法,比事后猜测 STL 实现要好得多。
  • 你不能问操作系统进程使用了​​多少内存吗?
  • 通常应用程序不会消耗它使用的任何内存。

标签: c++ memory-management stl map vector


【解决方案1】:

std::vector&lt;element&gt; 通常需要 3 个机器字总共 + sizeof(element) * capacity() 内存。对于典型的实现,开销包括指向向量开始、结束和当前大小的指针。元素本身存储在连续的内存中。 capacity() 通常最多可容纳两倍于实际数量的元素。

std::map&lt;element, int&gt; 通常需要大约 2 个机器字总共 + 3 个机器字每个元素 + [ sizeof(element) +sizeof (int) ] * num_elements 内存。对于典型的实现,开销由指向存储元素的指针组成。元素本身存储在二叉树中,并带有指向其父级和两个子级的指针。

根据这些经验法则,您只需知道每个字符串的平均字符数和字符串总数即可了解总内存消耗。

【讨论】:

  • 对于std::vector,你应该使用capacity,而不是元素的数量。如果std::map 使用池分配器(很多都这样做),那么它分配的节点可能比map 中的元素多得多。
  • @JamesKanze Tnx,更新了 capacity() 备注。那么对于std::map,有没有一种非侵入式的方式(即不记录分配器)来获取内存消耗?
  • 不了解实现,没办法;如果分配器使用节点池,则可能没有办法。这里最简单的解决方案是将全局operator newoperator delete 替换为跟踪分配的。 (默认分配器需要使用::operator new来获取它们分配的内存。)
  • 另外,您关于capacity() 的评论最多可以容纳两倍于实际元素数量的空间,这是不正确的。容量没有任何限制,只是它会以倍数增长。典型的倍数是 1.5 和 2,但更大(或更小)当然是允许的。
  • @JamesKanze 我反复提到“典型实施”,因为无论如何都没有强制实施。
【解决方案2】:

对于std::vectorstd::string,容量而不是大小会 是一个更好的近似值。对于基于节点的容器 (std::set, 等),你想要乘以节点的数量(大约是 元素)乘以每个节点的大小。然而,这只是准确的,如果 分配器不为节点使用优化的池分配器。

但是,如果您真的想知道正在使用多少内存,则可以使用 更好的策略是替换全局 operator newoperator delete,并跟踪实际分配。更 准确的方法是替换 mallocfree。形式上,这不是 允许,但在实践中,我从未遇到过这样的实现 它不起作用。另一方面,如果你替换mallocfree, 您必须自己实现实际的内存管理。如果你 替换operator newoperator delete,你可以使用mallocfree,这让它变得相当简单。

还要注意,每个分配都有一些固定开销。一个 100000 每个 10 字节的分配将消耗比 10 次分配,每次 100000 字节。

【讨论】:

  • 替换新的将给我在堆分配期间使用的内存。对于这种用法,我将不得不添加所有容器的开销,即 sizeof(vector) + sizeof(string) 等?
  • 这取决于您要测量的内容。替换 new 将让您在 C++ 中使用动态内存(但不是在您可能使用的任何 C 部分)。这就是我对“内存消耗”的理解。它不会计算堆栈上的局部变量std::vector 中的三个指针,但它会计算此类向量中所有字符串使用的所有内存(因为向量是动态分配的)。
猜你喜欢
  • 1970-01-01
  • 2022-06-14
  • 2014-09-02
  • 1970-01-01
  • 2016-09-26
  • 2016-05-24
  • 1970-01-01
  • 1970-01-01
  • 2010-10-12
相关资源
最近更新 更多