使用Windbg从转储文件中分析可能的内存泄漏

Using Windbg to analyze possible memory leak from a dump file

本文关键字:内存 泄漏 Windbg 转储 文件 使用      更新时间:2023-10-16

这个应用程序(原生c++)运行好几个小时,使用的内存保持在9MB左右几个小时,然后突然当我再次检查它去15,然后20,然后29等。每次检查之间可能会有几个小时的间隔,并且它也会在几个小时内保持相同的值。应用程序没有崩溃,所以我在任务管理器运行时生成了一个转储文件,希望在windbg或vstudio中分析它。

在VStudio中,我可以看到正在运行的线程,看到每个线程在转储时刻的特定行,看到局部变量等,但我不能(我猜我不知道如何)看到正在分配的是使用如此多的内存。

我打开了调试内存窗口,看到4个内存(1-4),但我不能理解它,我还没能找到一个关于如何使用该信息的好文档。

当使用windbg时,我同时加载windows符号和应用程序符号,但是当我输入:"!"我只看到两个堆,没有太多的信息可以继续。我已经遵循了几个教程如何使用windbg找到内存泄漏的成功,但他们都是简单的情况下,在所有情况下,要么应用程序启动/调试在windbg本身或附加到它,但我没有找到一个文档,其中生成的转储(不是崩溃转储)进行分析。

这可能吗?如果有的话,请给我一些如何进行的指导,提前谢谢你

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

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

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

另一种情况是,您的应用程序(可能在某些情况下)添加了新信息而没有删除旧信息,这增加了—从技术上和严格意义上讲,不是泄漏。但是它会堆积起来。

然而,我的猜测是你的应用程序是"重新启动"的东西,而不是清理自己。换句话说,它已经在堆上分配了一定数量的内存,然后"忘记"了所有内存,并重新开始使用新的内存集。这可能是一个bug,例如"增长这个对象"类型的函数,或者它可能是vector<int> *v = new vector<int>; ... v->push_back(x); ...缺少delete v;类型的东西。

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

查找包含指向对象的指针的容器对象(vector, map等)-检查元素的数量是否"看起来正确",以满足应用程序应该做的事情。

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

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