【问题标题】:C++ STL memory management in containers容器中的 C++ STL 内存管理
【发布时间】:2015-02-05 12:58:30
【问题描述】:

我正在编写一些关键任务代码,我必须确保它绝对没有内存泄漏。我编写了一个小函数,允许我在运行时检索内存使用情况,并在执行一些代码(应该无泄漏)之前和之后进行测量,以查看内存使用情况是否保持在同一水平。

在调试一段“泄漏”的代码时,我终于发现罪魁祸首是向量容器。

重现我所看到的最小代码如下:

vector<char*>* v = new vector<char*>();
int n = 1024*1024;
while (n--)
{
     v->push_back(new char[256]()); // A
}
for (vector<char*>::iterator it=v->begin() ; it!=v->end() ; ++it )
{
     delete[] (*it);
}
delete v;

如果您运行该代码(当然禁用编译器优化,-O0)并在最后放置一些陷阱以使程序不会退出(例如cin.ignore();),您会看到您的程序应该使用20Mb 左右的内存。

我想了解为什么会这样。有一行我用A 标记,如果你分配一个更大的字符数组,你会看到最后的“剩余”内存也更大。我不会将其称为泄漏本身,因为如果我分配和填充另一个 STL 容器,显然可以重用内存,但我仍然希望在代码完成时完全释放该内存。

有人能解释一下为什么这个内存还在被使用吗?我怎样才能真正“释放”它?

关于我的编译环境的一些细节:

Using clang++: Apple LLVM version 6.0 (clang-600.0.51) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix

Compiling with: g++ -std=c++11 -g -Wall -Wextra -Wpedantic -O0 main.cc -o main.out

【问题讨论】:

  • 为什么要使用vector 而不是vector> !?
  • 我只是使用该 char* 来分配任意数量的内存。实际上,您可以在那里使用任何类型的类型,并且仍然会观察到这种行为。
  • 您似乎期望内存管理器实际上会因为您释放它而从您的进程中释放内存,但这不是它通常的工作方式。无论如何,它只是虚拟内存,所以只要您的进程不使用它,它就不会占用任何 RAM。而且,正如您所说,如果您要求更多内存,则该内存将被重用。
  • @Deduplicator 您可以尝试使用 vector* 运行代码,并以您喜欢的方式分配所有内容。你仍然会观察到这种行为。
  • 在系统监视器等中查看内存使用情况绝对是检查泄漏的错误方法,您将到处寻找鬼魂。

标签: c++ vector memory-leaks stl


【解决方案1】:

首先,在您的场景中动态分配容器是没有任何借口的。为什么?

其次,它是char* 的容器,虽然它负责管理这些,但您对他们指出(或不指向)的任何内容负全部责任!

考虑使用vector&lt;char&gt;unique_ptr&lt;char&gt;string 或类似的元素类型,让vector 承担该任务。

最后,请记住,运行时系统使用自己的基于操作系统原语构建的分配器,因此分配内存不会直接转换为对操作系统的请求,释放/删除它也不会立即返回。
这样做效率会非常低

如果您真的想确保释放的内存返回给操作系统,您基本上有两种选择(这两种选择都涉及编写自己的分配器,或者找到并使用其他人构建的分配器):

  • 用您自己的替换可替换的全局分配和释放函数。
  • 仅对这些分配使用您自己的分配器(标准容器可识别分配器,这意味着您可以为每个容器提供自己的分配器)。

完成后,只需要求分配器将所有内容释放回操作系统即可。

【讨论】:

  • 命中所有正确的点。如果您必须保证没有泄漏,那么不使用 RAII 是很疯狂的,并且在不考虑运行时分配器缓存的情况下测量活动内存是非常有缺陷的。
  • 这与 RAII 无关。您可以尝试编写自己的代码,看看您最终会发现相同的行为。测量内存可能有很大缺陷,但你们不能保证我做的对还是错。我相信在解释整体情况/行为时我已经足够清楚了,这样就不会出现这种 cmets。无论如何,感谢 Puppy 的分配器提示,我会尝试实现自定义分配器,看看我是否可以通过这种方式解决问题。
  • @almosnow:是的,您不是直接询问 RAII,如果您将问题简化为您真正想要询问的内容(delete 之后缺少内存占用减少),我可以删除第一部分。这仍然是非常宝贵的建议,如果我没有非常突出地警告你,我会受到公正的批评。不过,我也在回答你实际提出的问题。
  • 谢谢@Deduplicator,但我要问的是为什么这个内存还在被使用? (因为它将来可能会被使用,是的,但是 STL 为什么以及如何做到这一点?)以及我应该如何确保它被释放?
  • 正如我所说,它被释放(回到运行时系统)。运行时系统(还)没有将其返回给操作系统有很好的理由。添加了一些关于如何强制将其释放回操作系统的内容。 (并且 STL 保留内存,分配器是标准库的一部分。顺便说一句:即使对于标准库的容器部分,STL 也是错误的术语,它是 STL 派生的。)
猜你喜欢
  • 2012-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 2012-02-27
  • 2012-08-04
相关资源
最近更新 更多