【问题标题】:Using Windbg to analyze possible memory leak from a dump file使用 Windbg 分析转储文件中可能存在的内存泄漏
【发布时间】:2014-10-16 23:17:37
【问题描述】:

这个应用程序(本机 C++)运行良好几个小时,使用的内存几个小时保持在 9MB 左右,然后当我再次检查时突然变为 15,然后是 20,然后是 29 等等。每次检查之间可能有几个小时,它几个小时内也保持相同的值。 该应用程序没有崩溃,因此我在从任务管理器运行时生成了一个转储文件,希望在 windbg 或 vstudio 中对其进行分析。

在 VStudio 中,我可以看到正在运行的线程,查看转储时每个线程所在的特定行,查看局部变量等,但我看不到(我想我不知道该怎么做)正在分配使用这么多内存。

我打开了 debug -memory 窗口,看到了 4 个内存 (1-4),但我无法理解,我也找不到关于如何使用这些信息的好文档。

使用 windbg 时,我同时加载了 windows 符号和应用程序符号,但是当我输入:“!heap -s”时,我只看到 2 个堆并且没有太多信息可以继续。我已经遵循了几个关于如何使用 windbg 成功查找内存泄漏的教程,但它们都是简单的案例,在所有情况下,应用程序要么在 windbg 本身上启动/调试,要么附加到它,但我还没有找到一个文档在哪里分析生成的转储(不是故障转储)。

这甚至可能吗?如果是这样,请提供一些有关如何进行的指示,提前谢谢您

【问题讨论】:

  • 使用 xperf 跟踪此问题:channel9.msdn.com/Shows/Defrag-Tools/…。它为您提供分配的调用堆栈。
  • 非常感谢,看起来确实很好,我会研究一下
  • 嘿,我忘了那个。您绝对应该从他的onedrive 下载 Andrew 的 xperf 脚本。在我看来,Collect Heap_Attach 预设可以为您提供有关您的场景的大部分信息。

标签: c++ memory-leaks windbg dump


【解决方案1】:

您描述的场景可能很难调试,无论您是在查看事后(转储)还是实时调试。

第一步是确定问题是否是真正的泄漏——比如内存没有被释放。确定这一点的最佳方法是使用检测代码来测量堆使用情况,跟踪进行了哪些分配,以及它们的大小,并报告(例如到日志文件)分配是什么,以及它们的大小。或者只是跟踪活动分配的数量。

在某些情况下内存没有严格泄漏,您的应用只是使用更多内存,例如来自堆的碎片 - 这会使堆增长,但从技术上讲它不是泄漏。这可能很难避免,例如字符串数组,并且您向每个字符串添加一个字符,每个字符串都需要稍大的空间。如果分配的字符串与许多其他分配穿插在一起,则每个字符串释放的空间将太小而无法容纳新的字符串。这种行为可能会创建小的或大的堆碎片,随着时间的推移,堆会增长——通常它最终会停止增长,但它可能需要相当长的时间,这取决于应用程序做什么,它从内存分配的频率。

另一种情况是您的应用程序(可能在某些情况下)正在添加新信息而不是删除旧信息,这加起来 - 从技术上和严格意义上说,不是泄漏。但它建立在堆上。

但是,我的猜测是您的应用程序正在“重新启动”某些东西,而不是自行清理。换句话说,它在堆上分配了一些内存,然后“忘记”了整个内存,并重新开始使用新的集合。这可能是例如“增长这个对象”类型函数中的错误,或者它可能是 vector<int> *v = new vector<int>; ... v->push_back(x); ... 缺少 delete v; 类型的东西。

不幸的是,就像我说的那样,调试这类东西并不容易。你真的必须至少知道你期望找到什么,以及堆中实际有什么。如果您有很多相同类型的对象,并且您可以识别对象,则可以挖掘内存转储并查看是否存在异常大量的特定类型元素。但这假设您可以从内存转储中识别出哪些对象是什么类型(如果您有具有虚函数的对象,则应该能够识别堆中的vptr,如果没有别的)。做到这一点并不容易,并且需要相当多的经验来查看内存转储以确定什么是什么,以及如何确定“这是正常的”和“这不是”。

查找包含指向对象的指针的容器对象(矢量、地图等) - 检查元素的数量是否“看起来正确”,以适合您的应用程序应该执行的操作。

您还可以在相关位置添加日志记录,例如打印出所选容器对象的大小。

有一些库/运行时工具可以帮助识别内存泄漏——我是一名 Linux 开发人员,所以我会使用 valgrind,但我知道也有一些 Windows 工具可以做这种事情。或者,就像我说的,实现检测内存使用的operator newoperator delete - 以最简单的形式只是一个向上和向下计数器[如果你有多个线程是原子的] 并跟踪有多少未完成的分配。如果你保持足够长的时间,你应该清楚你是否真的在泄漏东西。然后,您可以使其更复杂,并添加进行分配的位置(获取调用堆栈等)。和/或记录分配的大小等。

【讨论】:

  • +1 - 当超过某个内存提交阈值时,OP 可能会通过使用procmon 创建转储来缩小范围。运气好的话,他会得到内存实际增量发生时间的转储,并从中得出结论。
  • 谢谢 Mats,我现在真的相信这更像是一个堆碎片问题而不是泄漏,我做了很多字符串操作,(连接、查找、获取子字符串、替换等) .我还有一个地图容器,它存储指向对象的指针,但我每 2 秒打印一次它的大小,它与我的预期一致,它应该包含与工作线程数一样多的对象,并且它是一致的。我很欣赏你的cmets。显然 Windbg 并不是特别适合这种情况的最合适的工具,或者我打算如何使用它的文档比我想象的要稀缺。 :-)
猜你喜欢
  • 2013-10-31
  • 1970-01-01
  • 1970-01-01
  • 2013-05-24
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多