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


125

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

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

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

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


在避免泄漏方面,以下帖子提供了一些建议:http
//stackoverflow.com/questions/27492/c-memory-management


我将其与Visual Studio一起使用以检测内存泄漏。codeproject.com/KB/applications/visualleakdetector.aspx
tiboo 2010年

1
您searh valgrin(对于Linux)或deleaker(对于Windows),还可以看一下视觉检漏仪...
John Smith

查找内存泄漏的信息,请查看:theunixshell.blogspot.com/2013/11/…–
Vijay

Answers:


78

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


1
Valgrind现在也可以在OS X上运行,因此linux不是您唯一的选择。
Michael Anderson 2010年

1
适用于Linux(和OS X)的Valgrind。如果您使用windose-Deleaker-最好的!
约翰·史密斯

@JordiBunster:太好了!但是基于运行时。使用大型代码库(仅用C语言编写),您将主要测试程序的设计方式。攻击者可能需要数千小时才能阅读代码,以发现内存泄漏漏洞。我本来希望能有一个类似于Java的自动化工具来进行源代码分析。
user2284570

65

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

我将从这篇文章开始:https : //msdn.microsoft.com/zh-cn/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链接中还介绍了其他可以使用的技术。


关于此方法的注释:看来,只有在将纯C与malloc和free一起使用时,此方法才有效。如果您使用的是c ++的new和delete,则不会创建包含行号的详细报告。
Zach

2
@Zach:实际上,您也可以使它正常工作(无论如何,对于您自己实际编译的任何代码)-请参见social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/…中
Roman Starkov

这也可以在发布模式下工作吗?
合资公司

1
@ user3152463不。根据文档,它仅适用于调试版本:msdn.microsoft.com/en-us/library/e5ewb1h3
Dusty Campbell

这行是错误的:#define CRTDBG_MAP_ALLOC应该是:#define _CRTDBG_MAP_ALLOC
Fallso 2016年

37

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


1
和std:vector很好地替代了在分配数组(缓冲区)的相同函数中释放数组(缓冲区)的情况。
KJAWolf

4
至少std :: auto_ptr和boost :: shared_ptr仍然容易泄漏。
贾斯珀·贝克斯

5
只有当您错误地使用它们时,尽管我应该承认对于std :: auto_ptr错误地使用它是非常容易的。
Leon Timmermans

2
虽然这对于编码标准是很好的建议,但它不能回答问题。即使使用shared_ptr也可能导致循环依赖的泄漏。而且,您可以使用带有无限缓存策略的“泄漏”,这些策略甚至适用于垃圾收集的语言。
CashCow

@CashCow:你是正确的。虽然我在实践中还没有看到它,但这可能是因为我很少使用它们。在下面引用答案:“仅在绝对必要时才使用指针”。
Leon Timmermans

28

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

  1. 仅在绝对必要时使用指针
  2. 如果需要指针,请仔细检查是否有可能使用SmartPointer
  3. 使用GRASP Creator模式。

至于个人检测内存泄漏,我一直使用Visual Leak Detector,发现它非常有用。


2
视觉泄漏检测器已移至新站点vld.codeplex.com
KindDragon 2011年

VLD是一个非常好的检漏仪-我用VC完全推荐给大家++
Javid

1
点1的+1。这绝对是最基本的事情。不幸的是,在我看来,某些最大的“ C ++”库倾向于避开堆栈分配和/或RAII,而倾向于无处不在的Pointers,通常是没有明显的原因。因此,它们最终是“带有类的C”,而不是实际的C ++。
underscore_d

16

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

跟踪堆分配请求 -特别是有关唯一分配请求号的部分

_CrtSetDbgFlag

_CrtSetBreakAlloc

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



7

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


1
这个工具真的很完美!使用_CrtDumpMemoryLeaks()可以省去您的麻烦。和朋友,如MSDN中所述。只需一个include,它就能揭示一切!甚至可以在旧的C库中使用!
m_pGladiator

新版本(VS2013)是在这里:vld.codeplex.com
Dženan

7

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

如果您使用的是C ++,你可以随时避免使用新的明确的:你有vectorstringauto_ptr(预C ++ 11,改为unique_ptr在C ++ 11),unique_ptr(C ++ 11)和shared_ptr(C ++ 11)在你的军火库。

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


1
不要忘记的3或5的规则,然后
,私自Helfawi

4

那里有各种各样的替换“ malloc”库,这些库可让您最后调用一个函数,它会告诉您所有未释放的内存,并且在许多情况下,谁会首先分配(或新分配)它。


4

如果您使用的是MS VC ++,我可以从代码项目中强烈推荐这个免费工具:Jochen Kalmbach 的泄漏 查找器

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

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

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

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

Rational的(现在由IBM拥有)PurifyPlus以类似的方式说明了泄漏,但是我发现泄漏查找器工具实际上更易于使用,而且其好处还没有花费数千美元!


1
我检查了检漏仪,它看起来还不错,但是仅供参考,因为它包含内联汇编,因此无法在x64上按原样工作。
Zach


3

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


2

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

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

我对Unix方面的了解不足以提供帮助,尽管其他人也提供了帮助。

但是,不仅仅是泄漏检测,还有通过缓冲区溢出(或溢出)来检测内存损坏的概念。我认为,这种调试功能比普通的泄漏检测更加困难。如果您使用的是C ++对象,则这种类型的系统也会更加复杂,因为可以以多种方式删除多态类,从而在确定要删除的真正基址指针时会遇到麻烦。我知道没有一个好的“免费”系统可以对超限提供良好的保护。我们已经编写了一个系统(跨平台),发现它具有很大的挑战性。


2

我想提供一些我过去使用过的东西:一个基本的泄漏检查器,它是源代码级别的,并且是相当自动的。我之所以给予这个奖励有以下三个原因:

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

  2. 尽管有点残酷,但我不要让我感到尴尬。

  3. 即使它与某些win32挂钩相关联,也应易于缓解。

使用它时,必须注意以下new几点:不要做任何需要依靠在底层代码中的事情,当心关于在checkup.cpp顶部可能遗漏的情况的警告,意识到如果您打开在执行图像转储的代码上(并解决所有问题),您可能会生成一个巨大的文件。

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

再次提醒您,我已经有一段时间没有使用它了,您可能需要使用它。我将其放入以说明该想法。如果真的有足够的兴趣,我将愿意举一个示例,在此过程中更新代码,并用更好的名称替换下面URL的内容,其中包括一个语法清晰的清单。

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



2

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

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


2

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

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


内存验证程序还为调用您的本机代码的C#提供文件名和行号。x64版本处于测试版
Stephen Kellett 2010年

2

您是通过插值自己的syscall函数(记录调用,然后将调用传递给实函数)来计算分配和释放的吗?

这是跟踪来自未编写代码的调用的唯一方法。

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

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


1

至少对于MS VC ++,C运行时库具有一些我发现过去有用的功能。检查MSDN帮助中的_Crt*功能。



1

通用编码指南:

  • 资源应在分配它们的同一“层”(功能/类/库)处重新分配。
  • 如果这是不可能的,请尝试使用一些自动释放(增强共享指针...)

1

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

  1. 在为要分配的资源编写获取代码之后,立即编写发布代码。通过这种方法,它更难以“遗忘”,并且在某种意义上迫使人们认真考虑资源的生命周期,这些资源的生命周期是预先使用而不是暂时使用。

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


1

在此列表的顶部(当我阅读时)是valgrind。如果您能够在测试系统上重现泄漏,那么Valgrind就是很好的选择。我已经成功地使用了它。

如果您刚刚发现生产系统正在泄漏并且不知道如何在测试中重现该怎么办?在生产系统的状态中会捕获一些错误的证据,这可能足以提供问题的根源,以便您可以重现它。

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

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


1

在Motorola手机操作系统上,我们劫持了内存分配库以观察所有内存分配。它有助于发现内存分配的许多问题。由于预防胜于治疗,因此我建议使用静态分析工具,例如KlockworkPC-Lint


夹板是皮棉的较新替代品。
马克·凯格尔

@ user14788:Gimpel的PC-Lint产品比旧的Unix更现代lint。它具有许多特定于C ++的检查,而afaik splint则没有。请参阅答案中的链接(我将其从Lint重命名为PC-Lint)。

0

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



0

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

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


0

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


我不明白为什么减速会使结果无用。无论程序运行的速度如何,泄漏的内存肯定都会泄漏。这些工具的重点是发现泄漏,那么问题出在哪里呢?它运行得这么慢,以至于您无法物理地覆盖所有代码路径以进行概要分析吗?
underscore_d

-1

Mtrace似乎是Linux的标准内置工具。步骤如下:

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

mtrace对C ++并不是超级有用
Erin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.