C ++:使用std :: map进行微小的内存泄漏

时间:2009-09-04 14:20:13

标签: c++ memory memory-leaks

我正在编写一个自定义文本文件 - 数据解析器(类似JSON),我已经失去了很多时间试图在其中找到微小的内存泄漏。

我正在使用VC ++ 2008和命令_CrtMemCheckpoint和_CrtDumpMemoryLeaks来检查内存泄漏。

当我解析任何文件然后将其从内存中移除(与声称的任何其他内存一起)时,我得到一个16字节的内存泄漏,如下所示:

{290} normal block at 0x00486AF0, 16 bytes long.
Data: <  H `aH  hH  eH > C0 9A 48 00 60 61 48 00 18 68 48 00 D8 65 48 00

我设法将“违规”代码行缩小到这个范围:

classDefinitions[FastStr(cString)] = classDef;

classDefinitions std::map<FastStr, FSLClassDefinition*>,是我的解析器类的私有成员。

FastStr是一个简单的char *“包装器”,允许使用简单的c字符串作为键值;它没有内存泄漏(没有'新'命令)。 'FSLClassDefinition *'显然是一个简单的类指针,所以也没什么奇怪的。

现在是抓住了:

  1. 这一行在解析过程中执行了很多次,但我只泄漏了一个16字节的块。
  2. 如果我解析另一个文件,则没有另外16个字节的内存泄漏
  3. 如果我从内存中移除解析器(通过在{}代码块中删除它),然后在另一个代码块中重新创建它并让它解析另一个文件,然后我得到第二个 16字节内存泄漏。
  4. 这让我怀疑std :: map中存在内存泄漏;但它也可能是我的错误...我很确定这是违规行,因为如果我在之前停止解析,就没有内存泄漏; 内存泄漏如果我在这行后停止解析

    有人可以对此发表评论吗?

10 个答案:

答案 0 :(得分:10)

泄漏报告中的“{290}”是泄漏的内存块的内存分配的序列号。如果此序列号始终相同,则可以使用_crtBreakAlloc在命中分配序列号时导致调试器中断。从堆栈跟踪中,您可以找到此块的分配位置。一旦你知道它被分配到何处以及用于什么目的,它往往很容易确定为什么它没有被解除分配。

阅读Debug Heap文档以了解_crtBreakAlloc。

答案 1 :(得分:4)

让我们放开一件事:std::map中没有泄漏。代码是供每个开发人员查看的,现在它已被捕获。

如果你是对的,我会想象泄漏是在复制classDef或匿名FastStr对象。但是如果没有两者的代码,那就很难说了。你说他们都是指针,让我相信这条线只是一个症状,而不是实际的问题。如何显示一些代码?

答案 2 :(得分:4)

OldFart提供了解决问题的最终解决方案。

首先没有内存泄漏。调试器建议的内存位置位于STL xmemory 文件中, 43

return ((_Ty _FARQ *)::operator new(_Count * sizeof (_Ty)));

但它们不是真正的内存泄漏,只是VC ++ 2008调试器过于热心。我已经使用PurifyPlus(eval.version)进行了测试,并建议我的程序中没有内存泄漏。所以,这16个字节在程序退出时被删除了,虽然是的,STL早先没有这样做是不太好。

再次,感谢任何人的回复,首先从来没有问题!

答案 3 :(得分:2)

因为内存“泄漏”不会随着重复使用而缩放,所以它可能根本不是泄漏,而是由库分配的内存,直到内存分析完成后才会释放。内存通常由库分配,然后在后续调用中重用。因为图书馆无法判断您的哪个电话是最后一个,所以在您的节目退出或之后,它将不会释放它。

答案 4 :(得分:2)

到目前为止,我们还没有看到你的代码的一行,所以我们几乎不可能说“在所有广泛使用的STL实现中,std::map 没有在任何情况下泄漏记忆。“地图的析构函数运行后,所有的内存都已被释放。

当然,如果你使用的是一些模糊的专有STL实现,所有的赌注都会被取消,但除此之外,地图不是罪魁祸首。

当然,如果您怀疑map内存泄漏,请逐步完成。它是仅限标头的代码,因此它是可见的,可以像您自己的代码一样进行调试。在调试器中逐步执行它,查看它所做的分配,以及它们是否再次释放。

但更有可能的是,问题是FastStr或......代码中的其他内容。

尝试尽可能远离代码进行剥离,以获得重现错误的最小可能示例。

不要从一开始就运行完整的程序。如果您确定问题出在您发布的行中,那么您可以跳过所有初始解析,这将排除很多可能性。同样,删除之后发生的任何事情。如果这不能重现错误,则问题不在您已隔离的行中。

当你得到一个重现错误的小样本时,你也可以在这里发布一些内容供我们查看。

答案 5 :(得分:2)

如果将任何映射放在DLL中的文件范围内,MS Visual Studio 2008会抱怨内存泄漏。您无需对地图执行任何操作,只需声明它:

pound-include&lt; map&gt;

使用namespace std;

map&lt;短,长&gt; test_map;

...

检测到内存泄漏! 转储对象 - &gt; {143}正常阻塞位于0x00037140,长度为24个字节。  数据:&lt; @q @q @q&gt; 40 71 03 00 40 71 03 00 40 71 03 00 CD CD CD CD 对象转储完成。

在主程序中不会发生;如果地图在功能范围内,则不会发生。它可能是虚假的:在内存泄漏检测停止后,映射会被释放。

答案 6 :(得分:2)

只是为了确认位于DLL中文件范围的静态映射产生相同的微小内存泄漏情况(使用MS Visual Studio 2008)。

答案 7 :(得分:1)

我对此的直觉就是看看FastStr。你说它是一个简单的char *包装器,但它如何处理被复制(即内部char *被复制或重新创建)?你能告诉我们FastStr的代​​码吗?

除了您列出的证据表明某些静态数据或者您提到的'解析器'仅在您的测试代码块中创建一个,那么'parser'对象的成员可能是内存泄漏的来源。

另一个建议是在更具描述性的工具(例如valgrind)下运行代码以查明泄漏情况? Valgrind(或Purify)将告诉您代码中内存泄漏的确切位置。

答案 8 :(得分:1)

如果要消除这些潜在的true / false内存泄漏,则必须封装这些std容器和/或使用指针来实例化和删除它们,然后再检查堆。您必须这样做,因为如果内存泄漏来自另一个库,则您无法区分它是否是真正的泄漏。

答案 9 :(得分:0)

是否可以使用“valgrind --leak-check”在Linux下运行此代码?如果是这样,valgrind可能会告诉你内存泄漏了什么。