【问题标题】:memory leak detecting in C++ with/without Visual Leak Detector使用/不使用 Visual Leak Detector 在 C++ 中进行内存泄漏检测
【发布时间】:2011-04-03 15:16:16
【问题描述】:

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

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

如果是真的泄漏? 如果是,您知道为什么不报告文件/行吗? 同时我也在看this url

谢谢

检测到内存泄漏! 倾倒对象 -> {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 对象转储完成。

【问题讨论】:

标签: c++ visual-c++ memory-leaks visual-leak-detector


【解决方案1】:

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

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

  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 运行时:最后报告泄漏,但没有像样的方法报告调用堆栈。它拦截对 new、delete、...的调用的方法可能会导致与 3rd 方库(如 Qt、Boost、...)的组合出现问题

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

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

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

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

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

希望这会有所帮助。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 你好山魈!不幸的是 Visual Leak Detector 的输出。我使用的是来自 CodeProject 网站的旧版本,而不是来自 CODEPLEX 的旧版本 ..所以我认为我应该更新 + 我会检查设置。重要提示:你能轻轻地确认我写的是真的泄密吗?
    • 这不是 Visual Leak Detector 的输出——您将获得大量输出,其中包含分配对象的调用站点。您正在看到 Visual Studio 泄漏检测器的输出:msdn.microsoft.com/en-us/library/e5ewb1h3%28VS.80%29.aspx
    【解决方案3】:

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

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

    #define _CRTDBG_MAP_ALLOC
    #define _CRTDBG_MAP_ALLOC_NEW
    

    【讨论】:

    • 出于某种原因,这两个定义只是告诉我:c:\program files (x86)\microsoft visual studio 10.0\vc\include\crtdbg.h(1116) : {640} normal block at 0x000A5E28 , 12 字节长。除了泄漏......它并没有真正帮助
    • 设置这个会触发这个警告:c:\program files\microsoft visual studio 9.0\vc\include\crtdbg.h(1203) : warning C4985: 'operator new': attributes not present on previous声明
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 我知道这个工具,但恐怕我必须坚持使用VC++! :)
      • 没有什么可以阻止你在 gcc 中构建它,无论编译器如何,泄漏都是一样的
      • 假设他使用的应用程序+库兼容linux/gcc和windows/vc++
      【解决方案6】:

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

      FWIW,我怀疑 Purify 是 Valgrind 的灵感来源,已经提到过。

      【讨论】:

        【解决方案7】:

        如果分配编号(花括号中的编号)始终相同,this could help。基本上,它描述了如何使 VC++ 在尝试分配指定数量时生成断点。

        【讨论】:

        • 嗨 SpaceComboy,谢谢!我注意到了;我看到在某些项目中它保持不变,而在其他变化中。
        • @user311906:分配数只是计算所有堆分配。如果您的应用程序每次都以相同的顺序进行分配,则泄漏分配的数量不会改变,您可以使用它来追踪问题的根源。这比文件和行号更有用,因为它还会为您提供应用程序的当前状态。
        猜你喜欢
        • 2014-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-29
        • 1970-01-01
        • 1970-01-01
        • 2011-02-18
        相关资源
        最近更新 更多