8

我想在 Windows 中检测我的 C++ 程序的内存泄漏。我还在 MSDN 上阅读了有关mermoy 泄漏检测的文档,并且我也开始使用 Visual Leak Detector。

我对泄漏的报告有疑问。我期待一个带有行号的文件名,但我总是报告下面的文本。它具有泄漏描述的所有组成部分(块类型、内存地址、数据等),但文件名和行号除外。

如果是真的泄漏?如果是,您知道为什么不报告文件/行吗?与此同时,我也在看这个网址

谢谢

检测到内存泄漏!
倾倒对象 ->
{4723} 0x04AFB5B8 处的正常块,8 字节长。
 数据:2C 3F 00 00 28 3F 00 00
{1476} 0x04AC3B58 处的普通块,12 字节长。
 数据:00 CD CD CD EB 01 75 4C CA 3D 0B 00
对象转储完成。
4

7 回答 7

7

我研究了很多不同的跟踪内存泄漏的方法。他们都有自己的优点,但也有自己的缺点。

要了解它们的优缺点,我们必须了解不同的机制和要求:

  1. new、delete、malloc、free是如何拦截的?一些工具使用#define 来重新定义new、delete、malloc 和free,但这依赖于包含文件的正确顺序,并且如果一个类包含例如一个称为free 的方法(如Qt 中的情况)可能会出现问题。预处理器也会重新定义这个方法,这可能会导致编译错误或无法解析的外部。

    另一种方法是否决全局 new 和 delete 运算符。这是一个更清洁的解决方案,但是失败的是您有一个第 3 方库,它在库中放置了一个新的,但在标题中删除了(反之亦然)。

  2. 呼叫来源是如何确定的。如果 new,delete,... 使用#define 截获,通常是预处理器符号__FILE____LINE__用于获取泄漏源。但是,如果您的代码中有“通用”函数,例如 CreateString(),那么大多数泄漏将在这些通用函数中报告,这并不能真正帮助您。

    另一种方法是在运行时获取调用堆栈。使用 Windows StackWalk 功能可以很容易地完成,但根据我的经验,这非常非常慢。一个更快的替代方法是直接获取基指针,并依赖堆栈帧指针(您必须使用 /Oy- 编译以获取堆栈帧指针)。您可以像这样获得框架(基本)指针: _asm mov DWORD PTR [FramePtr], ebp。然后简单地循环并在循环中获取指令指针((ADDR *)FramePtr)[1];和下一个帧指针FramePtr = ((ADDR *)FramePtr)[0];

  3. 如何在准确的时刻报告泄漏。在我的例子中,我希望在应用程序结束时报告泄漏,但为了能够做到这一点,您需要在应用程序结束时有一个泄漏报告机制。这意味着,如果您想自己报告泄漏,则需要依赖在应用程序结束时销毁的全局变量(并在全局变量的析构函数中报告泄漏)。对于服务器类型的应用程序,您可能更感兴趣的是获取两个时间点之间的内存使用差异。

现在不同的泄漏系统:

  1. C RunTime:最后报告泄漏,但没有像样的方法报告调用堆栈。它拦截对 new、delete、...的调用的方法可能会导致与 3rd 方库(如 Qt、Boost、...)的组合出现问题

  2. 外部 Microsoft 实用程序(如 GFlags、UMDH、...):它们似乎只能记录两个时间点之间的差异。但是,调用堆栈似乎要好得多,尽管 GFlags 实用程序可能会在操作系统中设置可能导致应用程序严重减速的标志。

  3. 视觉检漏仪。似乎可以正确找到所有泄漏,但在我的情况下它不起作用,因为我有一个 3rd 方 DLL,它只是在其 DllUnload 中止进程(似乎是 Windows 7 特定的问题)。

  4. 我个人最喜欢的(我敢肯定人们不会同意我的观点)是编写自己的内存管理器。使用全局 new 和 delete 操作符可以很容易地完成拦截(有上面提到的可能的问题),并且您可以如上所述获取调用堆栈。这种替代方法还依赖于能够在应用程序的最后时刻执行代码。

在选择替代方案时,我发现以下方面对我的情况非常重要:

  • 我希望它在我的应用程序中无缝运行,以便在发生泄漏时立即通知每个开发人员。如果您将泄漏检查延迟到稍后使用 Purify 等外部实用程序的时刻,则泄漏发现将变得更加困难。
  • 我希望在应用程序结束时自动报告泄漏。
  • 我想从泄漏中获得尽可能多的信息(数据,调用堆栈,...)

希望这可以帮助。

于 2010-08-25T11:26:36.597 回答
5

这是 Visual Studio 自己的调试 CRT 的输出,而不是 Visual Leak Detector 的输出。首先确保您使用的是Codeplex的当前版本,并且您的项目中有 #included vld.h。您将获得更多信息的输出。

于 2010-08-25T10:34:58.770 回答
2

我调试了很多头文件后得到它。

这是在输出中启用文件/行号必须做的事情

#define _CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC_NEW
于 2010-08-25T13:24:37.520 回答
1

您是否在启用调试信息的情况下进行编译并确保泄漏检测器可以使用 pdb 文件?如果没有这些信息,它将无法提供行号。

于 2010-08-25T13:31:47.673 回答
0

如果分配编号(花括号中的编号)始终相同,这可能会有所帮助。基本上,它描述了如何使 VC++ 在尝试分配指定数量时生成断点。

于 2010-08-25T10:58:42.067 回答
0

Rational Purify可作为 VC++ 的付费插件使用,并且是一个非常好的泄漏(和其他问题)检测器。我以前在 Solaris 上经常使用它,而且非常好用和清晰。我也从其他人那里听到了关于用于 Visual Studio 的版本的好消息,但我从未真正尝试过。

FWIW,我怀疑 Purify 是 Valgrind 的灵感来源,这已经被提及了。

于 2010-08-25T10:44:41.290 回答
0

您应该使用Valgrind,它非常强大,并且可以正确解释程序中的泄漏位置。不过,您的程序可能需要使用 gcc 编译...

于 2010-08-25T09:57:42.120 回答