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

Posted

技术标签:

【中文标题】您如何检测/避免(非托管)代码中的内存泄漏? [关闭]【英文标题】:How do you detect/avoid Memory leaks in your (Unmanaged) code? [closed] 【发布时间】:2010-09-07 22:05:06 【问题描述】:

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

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

我知道这不是一个好方法,并且有一些问题。 (例如,如果您正在释放由平台 API 调用分配的内存,您的分配计数将与您的释放计数不完全匹配。当然,然后我们在调用分配内存的 API 调用时增加了计数器。)

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

【问题讨论】:

在避免泄漏方面,以下帖子有一些建议:http://***.com/questions/27492/c-memory-management 我在这里问了这个问题:http://***.com/questions/25730/what-is-the-best-free-memory-leak-detector-for-a-cc-program-and-its-plug-in-dlls 并通过Visual Leak Detector 取得了巨大的成功。 我用这个和 Visual Studio 来检测内存泄漏。 codeproject.com/KB/applications/visualleakdetector.aspx 你搜索 valgrin(适用于 linux)或 deleaker(适用于 windows),还要查看视觉泄漏检测器... 查找内存泄漏检查这里:theunixshell.blogspot.com/2013/11/… 【参考方案1】:

如果您的 C/C++ 代码可移植到 *nix,那么没有什么比 Valgrind 更好的了。

【讨论】:

Valgrind 现在也可以在 OS X 上运行,所以 linux 不是你唯一的选择。 Valgrind for Linux(和 OS X)。如果你使用windose - deleaker - 最好的! @JordiBunster:太好了!但基于运行时。使用大型代码库(以 C 语言编写),您将主要测试程序的设计方式。攻击者可以花费数千小时阅读代码以找到内存泄漏漏洞。我本来希望有一个类似于 javascript 的自动化源代码分析工具。【参考方案2】:

如果您使用的是 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 链接中解释了其他技术,也可以使用。

【讨论】:

关于此方法的注意事项:看来这仅在您使用带有 malloc 和 free 的纯 C 时才有效。如果您使用 c++ 的 new 和 delete,则不会创建包含行号的详细报告。 @Zach:实际上你也可以让它工作(无论如何,对于你自己编译的任何代码) - 请参阅social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/…中接受的答案 这也能在发布模式下工作吗? @user3152463 否。根据文档,它仅适用于调试版本:msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.71).aspx 这一行错误:#define CRTDBG_MAP_ALLOC 应该是:#define _CRTDBG_MAP_ALLOC【参考方案3】:

在 C++ 中:使用 RAII。像std::unique_ptrstd::shared_ptrstd::weak_ptr 这样的智能指针是你的朋友。

【讨论】:

和 std:vector 是一个很好的替代品,当数组(缓冲区)在相同的函数中被释放时,它们被分配。 至少 std::auto_ptr 和 boost::shared_ptr 仍然容易泄漏。 只有在你错误地使用它们的情况下,虽然我应该承认对于 std::auto_ptr 错误地使用它是很容易的。 虽然这是对编码标准的好建议,但它并不能回答问题。即使使用 shared_ptr 也会导致循环依赖的泄漏。而且你可以有无限缓存策略的“泄漏”,甚至适用于垃圾收集的语言。 @CashCow:你是对的。虽然我还没有在实践中看到它,但这可能是因为我很少使用它们。引用下面的答案,“仅在绝对必要时使用指针”。【参考方案4】:

作为 C++ 开发人员,这里有一些简单的指南:

    仅在绝对必要时使用指针 如果您需要指针,请仔细检查 SmartPointer 是否可行 使用 GRASP Creator 模式。

至于内存泄漏的检测我个人一直用Visual Leak Detector,觉得很有用。

【讨论】:

视觉检漏仪已移至新站点vld.codeplex.com VLD 是一个非常好的检漏仪——我完全推荐给所有使用 VC++ 的人 第 1 点 +1。这绝对是最根本的事情。不幸的是,在我看来,一些最大的“C++”库倾向于避开堆栈分配和/或 RAII 以支持指针无处不在,通常没有明显的原因。因此,它们最终成为“C with Classes”,而不是真正的 C++。【参考方案5】:

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

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

_CrtSetDbgFlag

_CrtSetBreakAlloc

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

【讨论】:

【参考方案6】:

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

【讨论】:

链接不再有效,请在此处尝试文档:support.microsoft.com/kb/2580960 并在此处下载:microsoft.com/en-us/download/details.aspx?id=26798【参考方案7】:

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

【讨论】:

这个工具真的很完美!它为您省去了使用 _CrtDumpMemoryLeaks(); 的麻烦。和朋友,如 MSDN 中所述。仅包含一个,它会暴露所有内容!甚至可以在旧的 C 库中使用! 新版本(适用于VS2013)在这里:vld.codeplex.com【参考方案8】:

调试模式下的 Microsoft VC++ 显示内存泄漏,尽管它不显示您的泄漏位置。

如果你使用 C++,你总是可以避免使用 new @ (C++11) 和 shared_ptr (C++11) 在你的武器库中。

当new不可避免时,尝试在构造函数中隐藏它(在析构函数中隐藏delete);这同样适用于 3rd 方 API。

【讨论】:

然后不要忘记规则 3 或 5【参考方案9】:

有各种各样的替代“malloc”库可以让你在最后调用一个函数,它会告诉你所有未释放的内存,在很多情况下,谁将它分配(或新)在第一名。

【讨论】:

【参考方案10】:

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

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

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

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

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

Rational(现在归 IBM)PurifyPlus 以类似的方式说明泄漏,但我发现泄漏查找器工具实际上更易于使用,而且成本不高!

【讨论】:

我检查了泄漏查找器,它看起来还不错,但仅供参考,它不适用于 x64,因为它包含内联汇编。【参考方案11】:

我自己没用过,但是我的 C 朋友告诉我Purify。

【讨论】:

【参考方案12】:

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

【讨论】:

【参考方案13】:

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

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

我对 unix 方面的了解不够,无法提供帮助,尽管同样,其他人也有。

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

【讨论】:

【参考方案14】:

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

    您可能会发现它很有用。

    虽然有点不礼貌,但我不会让这让我感到难堪。

    即使它与一些 win32 挂钩,也应该很容易缓解。

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

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

再次,请注意我已经有一段时间没有使用它了,您可能需要稍微使用一下。我把它放进去来说明这个想法。如果结果证明有足够的兴趣,我愿意制作一个示例,在此过程中更新代码,并将以下 URL 的内容替换为更好的内容,其中包括一个体面的语法颜色列表。

你可以在这里找到它:http://www.cse.ucsd.edu/~tkammeye/leakcheck.html

【讨论】:

【参考方案15】:

对于 Linux: 试试Google Perftools

有很多工具可以进行类似的 alloc/free 计数,Goolge Perftools 的优点:

相当快(与 valgrind 相比:非常快) 带有漂亮的结果图形显示 还有其他有用的功能:cpu-profiling、memory-usage profiling...

【讨论】:

移至:github.com/gperftools/gperftools【参考方案16】:

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

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

【讨论】:

【参考方案17】:

我建议使用软件验证中的Memory Validator。 事实证明,这个工具对帮助我追踪内存泄漏和改进我正在处理的应用程序的内存管理非常有用。

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

【讨论】:

内存验证器还为调用本机代码的 C# 提供文件名和行号。 x64 版本处于测试阶段【参考方案18】:

您是否通过插入您自己的系统调用函数来计算分配和释放,这些函数记录调用然后将调用传递给真正的函数?

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

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

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

【讨论】:

【参考方案19】:

至少对于 MS VC++,C 运行时库有几个我在过去发现有用的函数。查看 _Crt* 函数的 MSDN 帮助。

【讨论】:

【参考方案20】:

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

【讨论】:

【参考方案21】:

一般编码指南:

应在分配资源的同一“层”(函数/类/库)中释放资源。 如果这不可行,请尝试使用一些自动释放(提升共享指针...)

【讨论】:

【参考方案22】:

内存调试工具物超所值,但多年来我发现有两个简单的想法可以从一开始就防止大多数内存/资源泄漏。

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

    尽量少用 return。如果可能,分配的内容应该只在一个地方释放。获取资源和释放资源之间的条件路径应设计得尽可能简单明了。

【讨论】:

【参考方案23】:

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

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

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

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

【讨论】:

【参考方案24】:

在摩托罗拉手机操作系统上,我们劫持了内存分配库来观察所有的内存分配。它有助于发现内存分配的许多问题。 由于预防胜于治疗,我建议使用像Klockwork或PC-Lint这样的静态分析工具

【讨论】:

夹板是 lint 的新替代品。 @user14788:Gimpel 的 PC-Lint 产品比旧的 Unix lint 更现代。它有许多特定于 C++ 的检查,而 afaik 夹板没有。请参阅答案中的链接(我将其从 Lint 重命名为 PC-Lint)。【参考方案25】:

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

【讨论】:

【参考方案26】:

检测:

调试 CRT

避免:

智能指针,boehm GC

【讨论】:

【参考方案27】:

rmdebug 是一个不错的 malloc、calloc 和 reallloc 替代品,使用起来非常简单。它比 valgrind 快得多,因此您可以广泛地测试您的代码。当然它也有一些缺点,一旦你发现了泄漏,你可能仍然需要使用 valgrind 来查找泄漏出现的位置,并且你只能测试你直接执行的 malloc。如果一个 lib 因使用错误而泄漏,rmdebug 将找不到它。

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

【讨论】:

【参考方案28】:

大多数内存分析器都会将我的大型复杂 Windows 应用程序减慢到结果毫无用处的地步。有一种工具可以很好地发现我的应用程序中的漏洞:UMDH - http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx

【讨论】:

我不明白为什么减速会使结果毫无用处。无论程序运行的速度如何,肯定会泄漏内存。这些工具的重点是发现漏洞,那么问题出在哪里?它是否运行得如此缓慢,以至于您无法在物理上让它覆盖所有代码路径以对其进行分析?【参考方案29】:

Mtrace 似乎是 linux 的标准内置函数。步骤是:

    在 bash 中设置环境变量 MALLOC_TRACEMALLOC_TRACE=/tmp/mtrace.datexport MALLOC_TRACE;#include 添加到主源文件的顶部 mtrace(); 添加在 main 的开头,muntrace(); 在底部(return 语句之前) 使用 -g 开关编译程序以获取调试信息 运行你的程序 使用 mtrace your_prog_exe_name /tmp/mtrace.dat 显示泄漏信息 (我必须先在我的 Fedora 系统上使用 yum install glibc_utils  安装 mtrace perl 脚本)

【讨论】:

mtrace 对 C++ 不是很有帮助

以上是关于您如何检测/避免(非托管)代码中的内存泄漏? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

避免、发现和消除 Cocoa 中的内存泄漏

翻译: 如何使用 Xcode 的内存图调试器检测 iOS 内存泄漏并保留周期

如何避免本机反应中的内存泄漏?

如何使用模块化代码片段中的LeakCanary检测内存泄漏?

在 Spark 1.6.0 中调试“检测到托管内存泄漏”

如何避免内部类中的内存泄漏