如何检测/避免(非托pipe)代码中的内存泄漏?
在非托pipeC / C ++代码中,检测内存泄漏的最佳实践是什么? 和编码准则,以避免? (就好像它很简单;)
过去我们使用了一些愚蠢的方法:每个内存分配调用都有一个计数器增量,在释放时递减。 在程序结束时,计数器值应为零。
我知道这不是一个好方法,有几个捕获。 (例如,如果您释放由平台API调用分配的内存,则您的分配计数将不会与您的释放计数完全匹配。当然,当调用分配内存的API调用时,我们会增加计数器。
我期待着你的经验,build议,也许可以参考一些简化这个工具的工具。
如果你的C / C ++代码可以移植到* nix,那么比Valgrind更好的东西。
如果您使用的是Visual Studio,Microsoft提供了一些有用的function来检测和debugging内存泄漏。
我将从这篇文章开始: 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)相同的名称的成员,CRTdebuggingmacros将导致问题。
在上面引用的MSDN链接中还有其他一些技术可以使用。
在C ++中:使用RAII。 智能指针如std :: unique_ptr,std :: shared_ptr,std :: weak_ptr是你的朋友。
作为一个C ++开发人员,这里有一些简单的指导:
- 只有在绝对必要时才使用指针
- 如果您需要一个指针,请仔细检查一下SmartPointer是否有可能
- 使用GRASP Creator模式。
至于个人内存泄漏的检测,我一直使用Visual Leak Detector ,发现它非常有用。
我已经使用了DevStudio很多年了,总是令我惊讶的是,有多less程序员不了解debugging运行时库中提供的内存分析工具。 以下是一些开始使用的链接:
跟踪堆分配请求 – 特别是唯一分配请求编号部分
_CrtSetDbgFlag
_CrtSetBreakAlloc
当然,如果你不使用DevStudio,那么这不会特别有用。
我很惊讶没有人提到Windows操作系统的DebugDiag 。
它适用于发布版本,甚至在客户现场。
(您只需保留您的发行版本PDB,并将DebugDiagconfiguration为使用Microsoft公共符号服务器)
Visual Leak Detector是一个非常好的工具,虽然它不支持VC9运行时调用(例如MSVCR90D.DLL)。
debugging模式下的Microsoft VC ++显示内存泄漏,但不显示泄漏的位置。
如果你正在使用C ++,你总是可以避免显式使用new:你有vector
, string
, auto_ptr
(pre C ++ 11;被C ++ 11中的unique_ptr
替代), unique_ptr
(C ++ 11)和shared_ptr
(C ++ 11)在你的武库。
当新的是不可避免的,尝试隐藏在一个构造函数(和隐藏删除析构函数); 第三方API的作品也是一样的。
这里有各种各样的replace“malloc”库,它们可以让你在最后调用一个函数,它会告诉你所有未被logging的内存,在很多情况下,首先是谁(或者是new'ed) 。
如果你使用MS VC ++,我可以强烈推荐这个免费的工具:Jochen Kalmbach。
您只需将该类添加到您的项目中,然后调用
InitAllocCheck(ACOutput_XML) DeInitAllocCheck()
之前和之后的代码,你要检查泄漏。
一旦构build并运行代码,Jochen就提供了一个整洁的GUI工具,您可以在其中加载生成的.xmlleaks文件,然后在生成每个泄漏的调用堆栈中导航,以search有问题的代码行。
Rational(现在由IBM拥有)PurifyPlus以类似的方式说明了泄漏,但是我发现泄漏工具实际上更容易使用,并且不用花费数千美元!
从来没有用过它,但我的C朋友告诉我净化 。
如果您使用Visual Studio,则可能需要查看Bounds Checker 。 这不是免费的,但是在我的代码中发现泄漏非常有帮助。 它不只是做内存泄漏,但也GDI资源泄漏,WinAPI使用错误,和其他东西。 它甚至会告诉你泄漏的内存在什么地方被初始化,从而更容易追踪泄漏。
我认为这个问题不容易回答。 你如何真正接近这个解决scheme取决于你的要求。 你需要一个跨平台解决scheme吗? 你正在使用新/删除或malloc /免费(或两者)? 你真的只是在寻找“泄漏”,或者你想要更好的保护,如检测缓冲区溢出(或欠载)?
如果你在Windows的工作,MSdebugging运行时库有一些基本的debugging检测function,另一个已经指出,有几个包装可以包含在您的来源帮助泄漏检测。 find一个可以同时使用new / delete和malloc / free的包显然给你更多的灵活性。
我不太了解unix方面提供的帮助,尽pipe其他方面也有。
但是除了泄漏检测以外,还有通过缓冲区溢出(或溢出)来检测内存损坏的概念。 这种types的debuggingfunction比普通的泄漏检测更困难。 如果您使用C ++对象,则这种types的系统也会变得更加复杂,因为可以通过多种方式删除多态类,从而导致确定被删除的真正基指针的技巧。 我知道没有一个好的“免费”系统可以为超限提供适当的保护。 我们写了一个系统(跨平台),发现它非常具有挑战性。
我想提供一些我曾经使用过的东西:一个基本的泄漏检查程序,它是源代码级别,相当自动。 我给了这个原因有三个原因:
-
你可能会觉得它很有用。
-
虽然这有点冷淡,但我不让这让我难堪。
-
即使它绑定到一些win32挂钩,这应该是容易缓解。
有些东西在使用时必须小心:不要在底层代码中做任何需要依赖new
内容的东西,小心在leakcheck.cpp的顶部可能会漏掉的情况的警告,意识到如果你打开(并解决任何问题)图像转储的代码,您可能会生成一个巨大的文件。
该devise旨在允许您打开和closures检查器,无需重新编译包含其标题的所有内容。 在你想跟踪检查和重build一次的地方包含leakcheck.h。 之后,用或不用LEAKCHECK#define'd编译leakcheck.cpp,然后重新链接来打开和closures它。 包括unleakcheck.h将在文件中closures本地。 提供了两个macros:当您遍历不包含leakcheck.h的分配代码时,CLEARALLOCINFO()将避免报告相同的文件和行。 ALLOCFENCE()只是在生成的报告中删除一行,而不进行任何分配。
再一次,请意识到我有一段时间没有使用这个,你可能需要一点工作。 我正在试图说明这个想法。 如果结果是足够的兴趣,我会愿意做一个例子,更新过程中的代码,并用一个更好的东西,包括一个体面的语法颜色列表replace下面的URL的内容。
你可以在这里find它: http : //www.cse.ucsd.edu/~tkammeye/leakcheck.html
对于Linux:试用Google Perftools
Goolge Perftools的优点是有很多工具可以做类似的分配/免费计数:
- 相当快(与valgrind相比:非常快)
- 带有很好的graphics显示结果
- 还有其他有用的function:cpu-profiling,内存使用情况分析…
我会build议使用内存validation从软件validation。 这个工具certificate了自己对帮助我追踪内存泄漏并改进我正在处理的应用程序的内存pipe理非常有帮助。
一个非常完整和快速的工具。
你是通过插入你自己的系统调用函数来logging调用,然后把调用传递给实际函数来计算分配和释放吗?
这是您可以跟踪来自未编写代码的调用的唯一方法。
看看ld.so的手册页。 或者在一些系统上ld.so.1。
另外做Google LD_PRELOAD,你会在www.itworld.com上find一些有趣的文章来解释这个技术。
至less对于MS VC ++,C运行时库有几个function,我发现过去有帮助。 检查_Crt*
函数的MSDN帮助。
Paul Nettle的mmgr是我很久以来最喜欢的工具。 在源文件中包含mmgr.h,定义TEST_MEMORY,并且在运行应用程序的过程中,它提供了一个充满内存问题的文本文件。
一般编码指南:
- 应该在分配资源的同一个“层”(函数/类/库)中释放资源。
- 如果这是不可能的,尝试使用一些自动释放(boost共享指针…)
防止泄漏的最佳方法是使malloc最小化的程序结构。 这不仅从编程的angular度来看是好的,而且还提高了性能和可维护性。 我不是在讨论使用其他的东西来代替malloc,而是在重用对象的时候使用,并且在所有被传入的对象上保留非常明确的标签,而不是象往常习惯的那样用垃圾收集器像Java一样。
例如,我工作的程序有一堆表示图像数据的框架对象。 每个框架对象都有子数据,框架的析构函数可以释放这些数据。 程序保存所有分配的帧的列表,当它需要一个新的帧时,检查一个未使用的帧对象列表,看它是否可以重新使用现有的而不是分配一个新的。 在关机时,它只是遍历列表,释放一切。
列表顶部(当我读到)是valgrind。 如果您能够重现testing系统的泄漏,Valgrind非常出色。 我用它取得了巨大的成功。
如果您刚刚注意到生产系统现在正在泄漏而您不知道如何在testing中重现它呢? 在生产系统的状态中捕捉到一些什么是错误的证据,并且可以提供关于问题在哪里以便再现的问题。
这就是蒙特卡罗采样进入图像的地方。 阅读Raymond Chen的博客文章“穷人识别内存泄漏的方法”,然后检查我的实现(假定Linux只在x86和x86-64上testing过)
在摩托罗拉手机操作系统上,我们劫持了内存分配库来观察所有的内存分配情况。 这有助于发现内存分配的许多问题。 由于预防好于治疗,我build议使用静态分析工具,如Klockwork或PC-Lint
Valgrind是Linux的一个不错的select。 在MacOS X中,可以启用具有多个用于debugging内存分配问题的选项的MallocDebug库(请参阅malloc手册页,“ENVIRONMENT”部分有相关的详细信息)。 OS X SDK还包含一个名为MallocDebug的工具(通常安装在/ Developer / Applications / Performance Tools /中),可以帮助您监控使用情况和泄漏情况。
检测:
debuggingCRT
避免:
智能指针,boehm GC
一个不错的malloc,calloc和realllocreplace是rmdebug,使用起来非常简单。 然后valgrind快得多,所以你可以广泛地testing你的代码。 当然,它有一些缺点,一旦你发现泄漏,你可能仍然需要使用valgrind来find泄漏出现的位置,你只能testing你直接做的malloc。 如果因为使用错误而导致lib泄漏,rmdebug将无法find它。
内存debugging工具是值得黄金的重量,但多年来,我发现可以使用两个简单的想法,以防止大多数内存/资源泄漏编码的第一位。
-
在为要分配的资源编写采购代码后立即写入发行代码。 用这种方法很难“忘记”,在某种意义上,迫使人们认真思考资源的生命周期,而不是作为一个旁观者。
-
尽可能使用退货。 什么是分配应该只在可能的情况下在一个地方释放。 资源获取和释放之间的条件path应尽可能简单明了。
大多数内存分析器会使我的大型复杂Windows应用程序变慢,导致结果无用。 有一种工具可以很好地在我的应用程序中查找泄漏:UMDH – http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx
Mtrace似乎是Linux的标准内置版本。 步骤是:
- 在bash中设置环境variablesMALLOC_TRACE
MALLOC_TRACE = / TMP / mtrace.dat
导出MALLOC_TRACE; - 将#include <mcheck.h>添加到您主要源文件的顶部
- 添加mtrace(); 在main和muntrace()的开始处; 在底部(在返回语句之前)
- 使用-g开关编译程序以获取debugging信息
- 运行你的程序
- 显示泄漏信息
跟踪your_prog_exe_name /tmp/mtrace.dat
(我必须首先在yum install glibc_utils的 Fedora系统上安装mtrace perl脚本)