繁体   English   中英

C ++:带有std :: map的微小内存泄漏

[英]C++: tiny memory leak with std::map

我正在编写一个自定义文本文件数据解析器(类似于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;

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

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

现在这里是要抓住的地方:

  1. 该行在解析过程中执行了很多次,但是我只泄漏了一个16字节的块。
  2. 如果我解析另一个文件,就不会再有16字节的内存泄漏
  3. 如果我将解析器从内存中删除(通过将其放在{}代码块中),然后在另一个代码块中重新创建它并解析另一个文件,则第二个 16字节内存泄漏。

这使我怀疑std :: map;中存在内存泄漏; 但这也可能是我的错误...我很确定这是令人讨厌的行,因为如果之前停止解析,则不会发生内存泄漏; 如果我此行之后立即停止解析, 则会发生内存泄漏。

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

泄漏报告中的“ {290}”是泄漏的内存块的内存分配顺序号。 如果此序列号始终相同,则可以在命中该分配序列号时使用_crtBreakAlloc导致调试器中断。 从堆栈跟踪中,您可以找到此块的分配位置。 一旦知道了将其分配到何处以及出于什么目的,就很容易确定为什么不对其进行分配。

阅读Debug Heap文档以了解_crtBreakAlloc。

让我们解决一件事: std::map没有泄漏。 该代码可供每个开发人员查看,并且现在该代码已被捕获。

如果您是正确的,我可以想象泄漏是在复制classDef或匿名FastStr对象。 但是如果没有两者的代码,很难说。 您说它们都是指针,使我相信所讨论的行仅是一种症状,而不是实际的问题。 如何显示一些代码?

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

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

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

但是它们并不是真正的内存泄漏,只是VC ++ 2008调试器过于狂热。 我已经使用PurifyPlus(eval.version)进行了测试,并建议程序中没有内存泄漏。 因此,尽管程序可以在程序退出时删除这16个字节,但STL不能尽早删除并不是很好。

因此,再次感谢任何人的答复,从一开始就没有问题!

由于内存“泄漏”不会随着重复使用而扩展,因此可能根本不是泄漏,而是由库分配的内存,直到内存配置完成后才释放。 内存通常由库分配,然后在后续调用中重用。 由于该库无法告诉您哪个调用是最后一个调用,因此在程序退出或以后退出之前,它不会释放它。

到目前为止,我们还没有看到您的代码的任何一行,因此,对于我们来说,要说“在所有情况下, std::map不会泄漏内存, std::map不会泄漏内存”,这几乎是不可能的。 ” 映射的析构函数运行后,将释放其所有内存。

当然,如果您使用的是一些晦涩的专有STL实现,那么所有赌注都没有了,但否则,地图不是罪魁祸首。

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

但更可能的问题是FastStr或代码中的其他问题。

尝试从代码中剥离尽可能多的代码,以获取最小的示例来重现错误。

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

当您确实获得了一个重现该错误的小样本时,您还可以在此处发布一些内容以供我们浏览。

如果将任何映射放在DLL中的文件作用域上,则MS Visual Studio 2008将抱怨内存泄漏。 您不必对地图做任何事情,只需声明一下即可:

英镑包含<地图>

使用名称空间std;

map <short,long> test_map;

...

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

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

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

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

除了列出的证据表明存在某些静态数据外,或者如果您提到的“解析器”仅在测试代码块中创建一个,则“解析器”对象的成员很可能是内存泄漏的根源。

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

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

是否可以使用“ valgrind --leak-check”在Linux下运行此代码? 如果是这样,则valgrind可能能够向您显示什么内存正在泄漏。

暂无
暂无

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

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