【问题标题】:How to use Application Verifier to find memory leaks如何使用 Application Verifier 查找内存泄漏
【发布时间】:2010-06-02 07:52:37
【问题描述】:

我想使用标准实用程序在我的应用程序中查找内存泄漏。 以前我使用自己的内存分配器,但其他人(是的,你 AlienFluid)建议使用 Microsoft 的 Application Verifier,但我似乎无法让它报告我的泄漏。 我有以下简单的应用程序:

#include <iostream>
#include <conio.h>

class X
   {
   public:
      X::X() : m_value(123) {}
   private:
      int m_value;
   };

void main()
{
X *p1 = 0;
X *p2 = 0;
X *p3 = 0;

p1 = new X();
p2 = new X();
p3 = new X();
delete p1;
delete p3;
}

这个测试显然包含内存泄漏:p2 是新的但没有被删除。

我使用以下命令行构建可执行文件:

cl /c /EHsc /Zi /Od /MDd test.cpp
link /debug test.obj

我下载了 Application Verifier (4.0.0665) 并启用了所有检查。

如果我现在运行我的测试应用程序,我可以在 Application Verifier 中看到它的日志,但我没有看到内存泄漏。

问题:

  • 为什么应用程序验证程序不报告泄漏?
  • 或者应用程序验证程序不是真的要查找漏洞吗?
  • 如果没有哪些其他工具可用于在应用程序结束时清楚地报告泄漏(即不通过定期拍摄快照并比较它们,因为这在占用 1GB 或更多的应用程序中是不可能的),包括调用分配位置的堆栈(所以不是 CRT 末尾的简单泄漏报告)

如果我找不到合适的实用程序,我仍然必须依赖我自己的内存管理器(它做得很好)。

【问题讨论】:

  • 这就是这些工具的问题——除了我们真正需要的之外,它们什么都做... CRT 内存泄漏检测是否适合您,包括代码中的分配位置,但没有调用堆栈?这种情况下只需要重新定义new操作符并开启内存泄漏转储即可。
  • 问题是我有一个完美工作、自写的内存分配器,速度非常快,记录所有内存分配,包括调用堆栈,在应用程序结束时报告泄漏(包括调用堆栈),检查对于缓冲区溢出/下溢,...,但每个人(在 StackOverflow 上)似乎都表明您不能编写自己的内存管理器,因为标准的 CRT/Windows 之一已经足够好,并且有足够的实用程序来查找内存泄漏,覆盖,......但是,我似乎无法让它们工作。
  • 我也认为内存泄漏检测不是编写自己的内存分配器的原因。 CRT 提供除了堆栈跟踪之外的所有内容 - 如果您感兴趣,我可以将代码发布给您。
  • @Alex,泄漏检测不是唯一的原因。它也是缓冲区溢出/下溢,更容易发现内存损坏,......我正在尝试为我自己目前正在做的所有事情找到标准替代方案,泄漏检测只是其中之一。当然我对你的代码很感兴趣。可以发一下吗?
  • 应用程序验证程序在 exe 中找不到堆跳跃。它在其文档中这么说。它会发现堆损坏(即双重删除)以及其他问题,但根据您的问题,它可能很有用。

标签: c++ windows memory-leaks application-verifier


【解决方案1】:

CRT 内存泄漏检测(无堆栈跟踪):

// debug_new.h #pragma 一次 #include "crtdbg.h" #ifdef _DEBUG #ifndef DEBUG_NEW #define DEBUG_NEW 新(_NORMAL_BLOCK,__FILE__,__LINE__) #万一 #万一

所有 .cpp 文件:

#include “debug_new.h” ... // 在所有其他包含行之后: #ifdef _DEBUG #define new DEBUG_NEW #万一 ...

在程序初始化代码中写一次:

_CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);

在 MFC 中,所有这些都已在 MFC 标头中实现。您只需要确保每个 cpp 文件都包含这些行:

#ifdef _DEBUG #define new DEBUG_NEW #万一

限制:这仅捕获“新”内存泄漏,不会捕获由其他函数(如 malloc)引起的所有泄漏。

不要在 .h 文件内进行任何分配 - 它们将在没有源代码行的情况下打印,因为 DEBUG_NEW 是在所有 #include 行之后定义的。

【讨论】:

  • 这个技巧不适用于 malloc/free。 CRT 的包含文件之一与 MALLOC 和 FREE(#define free DEBUG_FREE 或类似的东西)做类似的事情,这给具有称为 free 的方法的类带来了问题(例如在 Qt 中)。另外,这也写出了内存泄漏,但没有调用堆栈,我真的需要调用堆栈。
【解决方案2】:

应用程序验证程序仅捕获 DLL 中的泄漏。尝试阅读泄漏复选框中的工具提示。就是这么说的。

【讨论】:

    【解决方案3】:

    我有一种感觉,Application Verifier 是退出路径的特殊情况,并且不会将这些标记为泄漏 - 毕竟,整个进程堆在进程退出时是空闲的。

    尝试编写另一个示例,在其中再次初始化相同的指针 - 基本上会丢失对先前分配的引用。这当然应该被标记。让我知道结果。

    此外,AppVerifier(如果您启用了所有选项)还应该捕获缓冲区溢出、下溢、写入标记为 RO 的堆栈位置等。

    【讨论】:

    • 顺便说一句,还可以尝试 Windows SDK 中 Windows 调试工具附带的“全局标志”实用程序。它有一堆堆标记等选项,它可能是你需要的。
    • 我尝试将指针设置为 0。应用程序验证程序中没有报告任何内容。尝试使用 GFLAGS 启用所有与内存相关的标志。没有任何报道。似乎根本不可能在退出时发生内存泄漏(CRT 中极少的泄漏报告除外)。看来我必须坚持自己的内存管理器。
    【解决方案4】:

    来自软件验证的内存验证器将捕获内存泄漏,并显示来自泄漏分配的完整调用堆栈。虽然它是一个商业产品,但它有一个试用期,因此程序员可以试用它,看看它是否物有所值。

    【讨论】:

      【解决方案5】:

      最简单的解决方案是一开始就不要写入泄漏或缓冲区溢出 - 在事件发生后检测它们真的是浪费精力。在我自己的代码中,多年来我在这些领域的问题为零。为什么?因为我使用 C++ 提供的机制来避免它们。例如:

      X *p1 = 0;
      p1 = new X();
      

      应该是:

      shared_ptr <X>  p1 = new X();
      

      您不再担心 p1 泄漏。更好的是,根本不要使用动态分配:

      X x1;
      

      对于缓冲区溢出,请始终使用 std::string 这样的类型,它会在输入时增长,或者如果它们不增长,则会检测到可能的溢出并警告您。

      我并不是在吹嘘自己在避免内存泄漏方面的能力——这些东西确实有效,并且允许您继续调试代码的业务 逻辑这一更困难的任务。

      【讨论】:

      • 许多不幸的人不得不处理遗留代码(未经测试的丑陋的 c 东西)并且无法重写所有内容。
      • 该问题要求检测内存泄漏的工具,答案以“在事件发生后检测它们真的是浪费精力”开头。最后一条语句“继续调试代码的业务逻辑这一更困难的任务”也可以这样说——为什么不编写不需要调试业务逻辑的代码呢?显然,OP 的示例旨在演示一个泄漏代码的案例,并且是一个问题 reg。如何检测它。改正代码当然是小菜一碟了。
      • 答案完全不知道现实世界存在内存泄漏,需要清除。正如@Sascha 所说,编写新代码是一回事,维护现有代码是另一回事。
      【解决方案6】:

      Visual Leak Detector (v2.2) 比 CRT 调试库更有用,因为它会显示用于内存分配的完整调用堆栈已导致泄漏。

      【讨论】:

      • 当使用 C++ 单音时,使用 VLD 是相当困难的
      【解决方案7】:

      Application Verifier 不是这项工作的正确工具,但我仍然建议您启用 Application Verifier 检查。 Windows 上的 等效项是UMDH(用户模式转储堆),它是一个命令行工具。

      我的一位前同事写了UMDH Gui 至少是为了一些可用性。

      【讨论】:

        猜你喜欢
        • 2015-09-19
        • 2012-02-27
        • 2011-07-05
        • 2017-12-30
        • 2018-05-31
        • 2019-08-20
        • 2014-03-26
        相关资源
        最近更新 更多