如何在C ++代码/项目中查找内存泄漏?
我是Windows平台上的C ++程序员。 我正在使用Visual Studio 2008。
我通常最终在内存泄漏代码。
通常我通过检查代码来发现内存泄漏,但是这很麻烦,并不总是一个好方法。
由于我买不起付费内存泄漏检测工具,所以我想让大家build议最好的方法来避免内存泄漏。
- 我想知道程序员如何find内存泄漏。
- 是否有任何标准或程序应该遵循,以确保程序中没有内存泄漏?
说明
你需要的东西
- 熟练使用C ++
- C ++编译器
- debugging器和其他调查软件工具
1
了解操作员的基本知识。 C ++运算符“new”分配堆内存。 “删除”运算符释放堆内存。 对于每一个“新”,你应该使用“删除”,这样你释放你分配的相同的内存:
char* str = new char [30]; // Allocate 30 bytes to house a string. delete [] str; // Clear those 30 bytes and make str point nowhere.
2
只有在删除后才能重新分配内存。 在下面的代码中,str通过第二次分配获取一个新地址。 第一个地址不可挽回地丢失了,它指向的30个字节也是如此。 现在他们不可能自由,而且你有内存泄漏:
char* str = new char [30]; // Give str a memory address. // delete [] str; // Remove the first comment marking in this line to correct. str = new char [60]; /* Give str another memory address with the first one gone forever.*/ delete [] str; // This deletes the 60 bytes, not just the first 30.
3
观看这些指针分配。 每个dynamicvariables(堆中分配的内存)都需要与一个指针相关联。 当一个dynamicvariables与其指针分离时,就不可能擦除。 同样,这导致内存泄漏:
char* str1 = new char [30]; char* str2 = new char [40]; strcpy(str1, "Memory leak"); str2 = str1; // Bad! Now the 40 bytes are impossible to free. delete [] str2; // This deletes the 30 bytes. delete [] str1; // Possible access violation. What a disaster!
4
请注意本地指针。 你在一个函数中声明的指针被分配到堆栈上,但它指向的dynamicvariables被分配到堆上。 如果您不删除它,程序退出该function后,它将保留:
void Leak(int x){ char* p = new char [x]; // delete [] p; // Remove the first comment marking to correct. }
五
“删除”后请注意方括号。 使用“删除”本身来释放一个对象。 使用“删除”[]与方括号来释放堆数组。 不要这样做:
char* one = new char; delete [] one; // Wrong char* many = new char [30]; delete many; // Wrong!
6
如果泄漏仍然允许 – 我通常与delecker寻找(在这里检查: http ://deleaker.com)。
谢谢!
您可以在代码中使用一些技术来检测内存泄漏。 最常见和最简单的检测方法是,定义一个macrosDEBUG_NEW,并使用它,以及__FILE__
和__LINE__
这样的预定义macros来定位代码中的内存泄漏。 这些预定义的macros告诉你内存泄漏的文件和行号。
DEBUG_NEW只是一个MACRO,通常定义为:
#define DEBUG_NEW new(__FILE__, __LINE__) #define new DEBUG_NEW
所以,无论你使用new
,它也可以跟踪文件和行号,可以用来定位内存泄漏在你的程序。
而__FILE__
, __FILE__
__LINE__
是预定义的macros ,它们分别在文件名和行号处使用它们!
阅读下面的文章,其中解释了与其他有趣的macros使用DEBUG_NEW技术,非常漂亮:
跨平台的内存泄漏检测器
从维基百科 ,
Debug_new引用C ++中的一种技术来重载和/或重新定义操作符new和operator delete,以拦截内存分配和释放调用,从而debugging程序以用于内存使用。 它通常涉及到定义一个名为DEBUG_NEW的macros,并使新成为新的(_ FILE _,_ LINE _)来logging关于分配的文件/行信息。 Microsoft Visual C ++在其Microsoft基础类中使用此技术。 有一些方法可以扩展此方法,以避免使用macros重定义,同时仍然能够在某些平台上显示文件/行信息。 这种方法有许多固有的局限性。 它只适用于C ++,不能通过malloc等C函数捕获内存泄漏。 但是,与一些更完整的内存debugging器解决scheme相比,它可以非常简单易用,速度也非常快。
有一些众所周知的编程技术可以帮助您最大限度地减less第一手获取内存泄漏的风险:
- 如果你必须做自己的dynamic内存分配,写成
new
和delete
总是成对的,并确保分配/解除分配代码被称为成对 - 如果可以的话,避免dynamic内存分配。 例如,尽可能使用
vector<T> t
而不是T* t = new T[size]
- 使用像智能指针一样的“智能指针”( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm )
- 我个人最喜欢的:确保你已经理解了指针所有权的概念,并确保在你使用指针的任何地方,你知道哪个代码实体是所有者
- 了解哪些构造函数/赋值运算符是由C ++编译器自动创build的,以及如果您拥有拥有指针的类(或者如果您的类包含指向它不具有的对象的指针的类),这意味着什么。
- 下载Windowsdebugging工具 。
- 使用
gflags
实用程序打开用户模式堆栈跟踪。 - 使用
UMDH
获取程序内存的多个快照。 在分配内存之前拍摄一张快照,并在您认为程序泄漏内存的地方之后拍摄第二张快照。 您可能希望在程序中添加暂停或提示,让您有机会运行UMDH
并拍摄快照。 - 再次运行
UMDH
,这次是在两个快照之间做差异的模式。 然后它将生成一个包含可疑内存泄漏调用堆栈的报告。 - 完成后恢复以前的
gflags
设置。
UMDH
会给你比CRTdebugging堆更多的信息,因为它正在监视整个过程的内存分配; 它甚至可以告诉你,如果第三方组件泄漏。
如果你使用gcc,可以使用gprof。
我想知道程序员如何发现内存泄漏
有些使用工具,有些使用工具,也可以通过同行代码审查
是否有任何标准或程序应该遵循,以确保程序中没有内存泄漏
对我来说:每当我创builddynamic分配的对象,我总是把释放的代码之后,然后填写代码之间。 如果你确信代码之间不会有例外,那么这样做可以。 否则,我使用try-finally(我不经常使用C ++)。
-
在Visual Studio中,有一个内置的内存泄漏检测器,称为C运行时库。 当主函数返回后程序退出时,CRT将检查应用程序的debugging堆。 如果你仍然有任何块在debugging堆上分配,那么你有内存泄漏..
-
本论坛讨论了一些避免C / C ++内存泄漏的方法。
在你的代码中searchnew
出现,并确保它们都出现在析构函数中具有匹配删除的构造函数中。 确保这是该构造函数中唯一可能的引发操作。 一个简单的方法是将所有的指针包装在std::auto_ptr
或boost::scoped_ptr
(取决于你是否需要移动语义)。 对于所有将来的代码,只要确保每个资源都由一个对象所拥有,这个对象将在其析构函数中清理资源。 如果你需要移动语义,那么你可以升级到一个支持r值引用的编译器(VS2010我相信)并创build移动构造函数。 如果你不想这样做,那么你可以使用各种棘手的技巧,包括认真使用交换,或者尝试Boost.Move库。
在Windows上,您可以使用CRTdebugging堆 。
是否有任何标准或程序应该遵循,以确保程序中没有内存泄漏。
是的,不要使用手动内存pipe理(如果你曾经手动delete
或delete[]
,那么你做错了)。 使用RAII和智能指针,将堆分配限制在绝对最小值(大部分时间,自动variables就足够了)。
回答你的问题的第二部分,
是否有任何标准或程序应该遵循,以确保程序中没有内存泄漏。
就在这里。 这是C和C ++之间的主要区别之一。
在C ++中,你不应该在你的用户代码中调用new
或者delete
。 RAII是一种非常常用的技术,它几乎解决了资源pipe理问题。 程序中的每个资源(资源都是任何必须获取的资源,然后发布:文件句柄,networking套接字,数据库连接,还有纯内存分配,在某些情况下还包括一对API调用(BeginX )/ EndX(),LockY(),UnlockY())应该被包装在一个类中,其中:
- 构造函数获取资源(如果资源是memroy分配,则通过调用
new
) - 析构函数释放资源,
- 可以防止复制和分配(通过使复制构造函数和赋值运算符为私有),或者实现正确工作(例如通过克隆底层资源)
这个类然后在本地,堆栈或类成员实例化,而不是通过调用new
和存储指针。
你通常不需要自己定义这些类。 标准库容器的行为也是如此,所以当向量被销毁时,任何存储在std::vector
对象都将被释放。 所以再次,不要将指针存储到容器(这将需要您调用new
和delete
),而是对象本身 (它给你免费的内存pipe理)。 同样,可以使用智能指针类轻松地包装刚刚分配给new
,并控制它们的生命周期。
这意味着当对象超出范围时,它会被自动销毁,并释放和清理其资源。
如果你在你的代码中一直这样做,你就不会有任何内存泄漏。 所有可能泄漏的东西都与一个析构函数绑定在一起,当控件离开声明对象的范围时,这个析构函数被保证被调用。
视觉泄漏检测器(Visual Leak Detector,VLD)是Visual C ++免费,强大的开源内存泄漏检测系统。
在Visual Studiodebugging器下运行程序时,Visual Leak Detector将在debugging会话结束时输出内存泄漏报告。 泄漏报告包括完整的调用堆栈,显示如何分配泄漏的内存块。 双击调用堆栈中的一行,跳转到编辑器窗口中的文件和行。
如果您只有崩溃转储,则可以使用Windbg !heap -l
命令,它将检测泄漏的堆块。 最好打开gflags选项:“创build用户模式堆栈跟踪数据库”,然后你会看到内存分配调用堆栈。
MTuner是一个免费的多平台内存分析,泄漏检测和分析工具,支持MSVC,GCC和Clang编译器。 function包括:
- 基于时间线的内存使用历史和实时内存块
- 强大的内存操作过滤基于堆,内存标签,时间范围等
- 完整源代码的手动testingSDK
- 通过命令行使用持续集成支持
- 调用堆栈树和树图导航
- 多得多。
用户可以使用GCC或Clang交叉编译器来分析任何软件定位平台。 MTuner内置支持Windows,PlayStation 4和PlayStation 3平台。
运行“Valgrind”可以:
1) 帮助识别内存泄漏 – 告诉你有多less内存泄漏,并指出泄漏内存分配的代码行。
2) 指出释放内存的错误尝试 (例如,不正确的“删除”)
使用“Valgrind”的说明
1) 在这里获得valgrind。
1)用-g标志编译你的代码
3)在你的shell运行:
valgrind --leak-check=yes myprog arg1 arg2
“myprog”是你编译的程序,“arg1”,“arg2”是你程序的参数。
4)结果是一个malloc / new的调用列表,没有后续的免费删除调用。
例如:
==4230== at 0x1B977DD0: malloc (vg_replace_malloc.c:136) ==4230== by 0x804990F: main (example.c:6)
告诉你在哪一行malloc(没有被释放)被调用。
正如其他人指出的那样,确保每一个“新”/“”malloc“调用你有一个后续的”删除“/”免费“的电话。
AddressSanitizer (ASan)是一个快速的内存错误检测器。 它发现在C / C ++程序中使用后释放和{堆,堆栈,全局}缓冲区溢出错误。 它发现:
- 免费使用(悬挂指针解除引用)
- 堆缓冲区溢出
- 堆栈缓冲区溢出
- 全局缓冲区溢出
- 返回后使用
- 初始化顺序错误
这个工具非常快。 仪器化程序的平均减速是〜2倍。
除了其他方法中提供的工具和方法之外,还可以使用静态代码分析工具来检测内存泄漏(以及其他问题)。 免费的一个强大的工具是Cppcheck。 但是还有很多其他工具可用。 维基百科有一个静态代码分析工具的列表。
这可能会帮助只想使用Visual Studio进行泄漏检测的人。 VS 2015及以上版本的“诊断工具”现在有了很大的改进。 也尝试过称为“Deleaker”的工具,但Visual Studio工具同样不错。 看下面的video帮助我开始了它。