如何检测/避免(非托管)代码中的内存泄漏?

时间:2008-09-05 12:18:01

标签: c++ c memory-leaks

在非托管C / C ++代码中,检测内存泄漏的最佳做法是什么?和编码指南,以避免? (好像就这么简单;)

过去我们使用了一些愚蠢的方法:每次内存分配调用都有一个计数器增量,而在释放时减少。在程序结束时,计数器值应为零。

我知道这不是一个好方法,而且有一些捕获。 (例如,如果释放由平台API调用分配的内存,则分配计数与释放计数不完全匹配。当然,我们在调用分配内存的API调用时递增计数器。)

我期待您的经验,建议以及对简化此工具的工具的一些参考。

29 个答案:

答案 0 :(得分:78)

如果你的C / C ++代码可以移植到* nix,那么很少有东西比Valgrind好。

答案 1 :(得分:64)

如果您使用的是Visual Studio,Microsoft提供了一些有用的功能来检测和调试内存泄漏。

我将从这篇文章开始: https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.140).aspx

以下是这些文章的快速摘要。首先,包括这些标题:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

然后你需要在程序退出时调用它:

_CrtDumpMemoryLeaks();

或者,如果您的程序每次都不在同一个地方退出,您可以在程序开始时调用它:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

现在程序退出所有未自由的分配将在输出窗口中打印,同时分配它们的文件和分配事件。

此策略适用于大多数程序。但是,在某些情况下变得困难或不可能。使用在启动时进行一些初始化的第三方库可能会导致其他对象出现在内存转储中,并且可能难以跟踪泄漏。此外,如果您的任何类具有与任何内存分配例程(例如malloc)同名的成员,则CRT调试宏将导致问题。

上面引用的MSDN链接中解释的其他技术也可以使用。

答案 2 :(得分:37)

在C ++中:使用RAII。 std::unique_ptrstd::shared_ptrstd::weak_ptr等智能指针是您的朋友。

答案 3 :(得分:28)

作为C ++开发人员,这里只是一些指导原则:

  1. 仅在绝对必要时使用指针
  2. 如果您需要指针,请仔细检查SmartPointer是否可能
  3. 使用GRASP Creator模式。
  4. 至于个人检测内存泄漏,我总是使用Visual Leak Detector并发现它非常有用。

答案 4 :(得分:16)

我已经使用DevStudio太多年了,我总是惊讶于有多少程序员不了解调试运行时库中可用的内存分析工具。以下是一些入门链接:

Tracking Heap Allocation Requests - 特别是有关唯一分配请求编号的部分

_CrtSetDbgFlag

_CrtSetBreakAlloc

当然,如果您没有使用DevStudio,那么这将不会特别有用。

答案 5 :(得分:10)

我很惊讶没有人提到Windows操作系统的DebugDiag 它适用于发布版本,甚至适用于客户站点 (您只需保留发布版本的PDB,并将DebugDiag配置为使用Microsoft公共符号服务器)

答案 6 :(得分:7)

Visual Leak Detector是一个非常好的工具,虽然它不支持VC9运行时调用(例如MSVCR90D.DLL)。

答案 7 :(得分:7)

调试模式下的Microsoft VC ++显示内存泄漏,但它没有显示泄漏的位置。

如果您使用的是C ++,则可以始终避免明确使用新内容:您拥有vectorstringauto_ptr(预先C ++ 11;替换为unique_ptr in你的武器库中的C ++ 11),unique_ptr(C ++ 11)和shared_ptr(C ++ 11)。

当new不可避免时,尝试将其隐藏在构造函数中(并在析构函数中隐藏delete);同样适用于第三方API。

答案 8 :(得分:4)

那里有各种替换“malloc”库,允许你在最后调用一个函数,它会告诉你所有不同意的内存,并且在很多情况下,是谁在malloced(或neweded)它第一名。

答案 9 :(得分:4)

如果您使用的是MS VC ++,我强烈推荐使用代码项目中的这个免费工具: Jochen Kalmbach leakfinder

您只需将该类添加到项目中,然后调用

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

要检查泄漏的代码之前和之后。

一旦构建并运行代码,Jochen就会提供一个简洁的GUI工具,您可以在其中加载生成的.xmlleaks文件,并在生成每个泄漏的调用堆栈中导航以查找有问题的代码行。

Rational(现在由IBM拥有)PurifyPlus以类似的方式说明泄漏,但我发现泄漏探测器工具实际上更容易使用,奖励它不花费数千美元!

答案 10 :(得分:3)

如果您使用的是Visual Studio,可能值得查看Bounds Checker。它不是免费的,但它在我的代码中发现泄漏非常有帮助。它不仅会造成内存泄漏,还会造成GDI资源泄漏,WinAPI使用错误以及其他问题。它甚至可以显示泄漏的内存被初始化的位置,从而更容易追踪泄漏。

答案 11 :(得分:3)

从未使用过它,但我的C朋友告诉我Purify

答案 12 :(得分:2)

我想提供过去曾经使用的东西:一个基本的泄漏检查器,它是源级别并且相当自动化。 我之所以放弃它有三个原因:

  1. 您可能会发现它很有用。

  2. 虽然它有点夸张,但我不会让那个让我感到尴尬。

  3. 即使它与某些win32挂钩绑在一起,也应该很容易缓解。

  4. 有些事情在使用时必须小心:不要在底层代码中执行任何需要依赖new的事情,要注意泄漏检查顶部可能遗漏的案例警告.cpp,意识到如果你打开(并解决任何问题)执行图像转储的代码,你可能会生成一个巨大的文件。

    该设计旨在允许您打开和关闭检查器,而无需重新编译包含其标题的所有内容。在您要跟踪检查和重建一次的地方包含leakcheck.h。此后,使用或不使用LEAKCHECK #define'd编译leakcheck.cpp,然后重新链接以打开和关闭它。包括unleakcheck.h将在文件中本地关闭它。提供了两个宏:当遍历分配不包含leakcheck.h的代码时,CLEARALLOCINFO()将避免报告相同的文件和行。 ALLOCFENCE()只是在生成的报告中删除一行而不进行任何分配。

    再次,请注意我暂时没有使用它,你可能需要稍微使用它。我正在放弃它来说明这个想法。如果事实证明有足够的兴趣,我愿意提出一个例子,更新流程中的代码,并用更好的内容替换以下URL的内容,包括一个体面的语法颜色列表。

    您可以在此处找到它:http://www.cse.ucsd.edu/~tkammeye/leakcheck.html

答案 13 :(得分:2)

我建议您使用来自软件验证的Memory Validator。 该工具证明了它非常有用,可以帮助我跟踪内存泄漏并改进我正在处理的应用程序的内存管理。

一个非常完整和快速的工具。

答案 14 :(得分:2)

防止泄漏的最佳方法是程序结构,它最大限度地减少了malloc的使用。这不仅从编程角度来看是好的,而且还提高了性能和可维护性。我不是在谈论使用其他东西代替malloc,而是在重用对象和保持非常明确的选项卡来传递所有被传递的对象,而不是像通常习惯于使用垃圾收集器的语言那样分配。像Java一样。

例如,我工作的程序有一堆表示图像数据的框架对象。每个帧对象都有子数据,帧的析构函数可以释放。程序保留所有已分配帧的列表,当需要新帧时,检查未使用的帧对象列表,以查看它是否可以重用现有帧,而不是分配新帧。在关闭时,它只是遍历列表,释放所有内容。

答案 15 :(得分:2)

对于Linux:  试试Google Perftools

有许多工具可以进行类似的分配/自由计数,Goolge Perftools的优点:

  • 相当快(与valgrind相比:非常快)
  • 附带精美的结果图形显示
  • 还有其他有用的功能:cpu-profiling,内存使用情况分析......

答案 16 :(得分:2)

我认为这个问题没有简单的答案。您如何真正接近此解决方案取决于您的要求。您需要跨平台解决方案吗?您使用new / delete或malloc / free(或两者)吗?您是否真的在寻找“泄漏”或者您是否想要更好的保护,例如检测缓冲区溢出(或欠载)?

如果您正在Windows端工作,MS调试运行时库具有一些基本的调试检测功能,而另一个已经指出,有几个包装器可以包含在您的源中以帮助进行泄漏检测。找到一个可以同时使用new / delete和malloc / free的软件包显然可以提供更大的灵活性。

我不太了解unix方面提供帮助,尽管其他人也有。

但是,除了泄漏检测之外,还有通过缓冲区溢出(或欠载)检测内存损坏的概念。我认为这种类型的调试功能比普通的泄漏检测更难。如果使用C ++对象,这种类型的系统也会变得更加复杂,因为可以以不同的方式删除多态类,从而导致确定正在删除的真正基指针的棘手问题。我知道没有一个好的“免费”系统能够为超支提供良好的保护。我们已经编写了一个系统(跨平台)并发现它非常具有挑战性。

答案 17 :(得分:2)

你是通过插入你自己的记录调用的系统调用函数然后将调用传递给真实函数来计算alloc和frees吗?

这是您可以跟踪源自您尚未编写的代码的电话的唯一方法。

查看ld.so的手册页。或者在某些系统上使用ld.so.1。

也可以使用Google LD_PRELOAD,您可以在www.itworld.com上找到一些有趣的文章来解释这项技术。

答案 18 :(得分:1)

Paul Nettle's mmgr是我长期以来最喜欢的工具。在源文件中包含mmgr.h,定义TEST_MEMORY,它会提供一个文本文件,其中包含在应用程序运行期间发生的内存问题。

答案 19 :(得分:1)

一般编码指南:

  • 资源应该在分配它们的同一“层”(函数/类/库)中释放。
  • 如果无法做到这一点,请尝试使用一些自动释放(boost共享指针......)

答案 20 :(得分:1)

至少对于MS VC ++,C Runtime库有几个我以前发现有用的功能。查看_Crt*函数的MSDN帮助。

答案 21 :(得分:1)

在摩托罗拉手机操作系统上工作时,我们劫持了内存分配库,以观察所有内存分配。它有助于在内存分配方面找到很多问题。 由于预防比固化更好,我建议使用静态分析工具,如KlockworkPC-Lint

答案 22 :(得分:1)

内存调试工具非常值得用金,但多年来我发现可以使用两个简单的想法来防止大多数内存/资源泄漏被编码。

  1. 在为要分配的资源编写获取代码后立即编写发布代码。使用这种方法更难以“忘记”,并且在某种意义上迫使人们认真考虑前期使用的资源的生命周期而不是放在一边。

  2. 尽可能使用return作为陪练。分配的内容应尽可能在一个地方释放。获取资源和发布之间的条件路径应尽可能简单明了。

答案 23 :(得分:1)

在这个列表的顶部(当我读到它时)是valgrind。如果您能够在测试系统上重现泄漏,Valgrind非常出色。我用它非常成功。

如果您刚刚注意到生产系统现在正在泄漏并且您不知道如何在测试中重现它,该怎么办?在生产系统的状态中捕获了一些错误的证据,它可能足以提供问题所在的洞察力,以便您可以重现它。

这就是蒙特卡罗采样的地方。阅读Raymond Chen的博客文章, “穷人识别内存泄漏的方法”然后检查我的实现(假设Linux,仅在x86和x86-64上测试)

http://github.com/tialaramex/leakdice/tree/master

答案 24 :(得分:0)

检测:

调试CRT

避免:

智能指针,boehm GC

答案 25 :(得分:0)

Valgrind是Linux的不错选择。在MacOS X下,您可以启用MallocDebug库,该库有几个用于调试内存分配问题的选项(请参阅malloc联机帮助页,“ENVIRONMENT”部分有相关详细信息)。 OS X SDK还包括一个名为MallocDebug的工具(通常安装在/ Developer / Applications / Performance Tools /中),可以帮助您监控使用情况和泄漏。

答案 26 :(得分:0)

一个不错的malloc,calloc和reallloc替换是rmdebug,它使用起来非常简单。那么valgrind要快得多,所以你可以广泛地测试你的代码。当然它有一些缺点,一旦你发现泄漏,你可能仍然需要使用valgrind找到泄漏出现的位置,你只能测试你直接做的mallocs。如果因为你使用错误而导致lib泄漏,则rmdebug将无法找到它。

http://www.hexco.de/rmdebug/

答案 27 :(得分:0)

大多数内存分析器将我的大型复杂Windows应用程序放慢到结果无用的程度。有一种工具适用于在我的应用程序中查找泄漏:UMDH - http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx

答案 28 :(得分:-1)

Mtrace似乎是linux的标准内置版本。步骤是:

  1. 在bash中设置环境变量MALLOC_TRACE MALLOC_TRACE = / TMP / mtrace.dat
    导出MALLOC_TRACE;
  2. #include&lt; mcheck.h&gt; 添加到主要源文件的顶部
  3. 在main的开头添加 mtrace(); ,在底部添加 muntrace(); (在return语句之前)
  4. 使用-g开关编译程序以获取调试信息
  5. 运行您的程序
  6. 显示泄漏信息,其中 mtrace your_prog_exe_name /tmp/mtrace.dat
     (我必须首先使用 yum install glibc_utils 在我的fedora系统上安装mtrace perl脚本)