如何检测C++内存泄漏
1、内存泄漏简介及后果wikipedia中这样定义内存泄漏:在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。 最难捉摸也最难检测到的错误之一是内存泄漏,即未能正确释放以前分配的内存的 bug。 只发生一次的小的内存泄漏可能不会被注意,但泄漏大量内存的程序或泄漏日益增多的程序可能会表现出各种征兆:从性能不良(并且逐渐降低)到内存完全用尽。 更糟的是,泄漏的程序可能会用掉太多内存,以致另一个程序失败,而使用户无从查找问题的真正根源。 此外,即使无害的内存泄漏也可能是其他问题的征兆。 内存泄漏会因为减少可用内存的数量从而降低计算机的性能。最终,在最糟糕的情况下,过多的可用内存被分配掉导致全部或部分设备停止正常工作,或者应用程序崩溃。内存泄漏可能不严重,甚至能够被常规的手段检测出来。在现代操作系统中,一个应用程序使用的常规内存在程序终止时被释放。这表示一个短暂运行的应用程序中的内存泄漏不会导致严重后果。 在以下情�,内存泄漏导致较严重的后果:
下面我们通过以下例子来介绍如何检测内存泄漏问题: #include <stdlib.h> #include <iostream> using namespace std; void GetMemory( char *p, int num) { p = ( char *) malloc ( sizeof ( char ) * num); //使用new也能够检测出来 } int main( int argc, char ** argv) { char *str = NULL; GetMemory(str, 100); cout<< "Memory leak test!" <<endl; //如果main中存在while循环调用GetMemory //那么问题将变得很严重 //while(1){GetMemory(...);} return 0; } 实际中不可能这么简单,如果这么简单也用不着别的方法,程序员一眼就可以看出问题,此程序只用于测试。 2、Windows平台下的内存泄漏检测2.1、检测是否存在内存泄漏问题Windows平台下面Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法,原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏。在vs中启用内存检测的方法如下:
通过包括 crtdbg.h,将 malloc 和 free 函数映射到它们的调试版本,即 _malloc_dbg 和 _free_dbg,这两个函数将跟踪内存分配和释放。 此映射只在调试版本(在其中定义了 _DEBUG)中发生。 发布版本使用普通的 malloc 和 free 函数。 #define 语句将 CRT 堆函数的基版本映射到对应的“Debug”版本。 并非绝对需要该语句;但如果没有该语句,内存泄漏转储包含的有用信息将较少。
此时,完整的代码如下: + View Code 当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在“输出”窗口中显示内存泄漏信息。 内存泄漏信息如下所示:
如果没有使用 #define _CRTDBG_MAP_ALLOC 语句,内存泄漏转储将如下所示:
未定义 _CRTDBG_MAP_ALLOC 时,所显示的会是:
定义了 _CRTDBG_MAP_ALLOC 时,还会显示在其中分配泄漏的内存的文件。 文件名后括号中的数字(本示例中为 10)是该文件中的行号。 注意:如果程序总是在同一位置退出,调用 _CrtDumpMemoryLeaks 将非常容易。 如果程序从多个位置退出,则无需在每个可能退出的位置放置对 _CrtDumpMemoryLeaks 的调用,而可以在程序开始处包含以下调用: + View Code 该语句在程序退出时自动调用 _CrtDumpMemoryLeaks。 必须同时设置 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF 两个位域,如前面所示。 2.2、定位具体的内存泄漏地方通过上面的方法,我们几乎可以定位到是哪个地方调用内存分配函数malloc和new等,如上例中的GetMemory函数中,即第10行!但是不能定位到,在哪个地方调用GetMemory()导致的内存泄漏,而且在大型项目中可能有很多处调用GetMemory。如何要定位到在哪个地方调用GetMemory导致的内存泄漏? 定位内存泄漏的另一种技术涉及在关键点对应用程序的内存状态拍快照。 CRT 库提供一种结构类型 _CrtMemState,您可用它存储内存状态的快照:
若要在给定点对内存状态拍快照,请向 _CrtMemCheckpoint 函数传递 _CrtMemState 结构。 该函数用当前内存状态的快照填充此结构:
通过向 _CrtMemDumpStatistics 函数传递 _CrtMemState 结构,可以在任意点转储该结构的内容:
若要确定代码中某一部分是否发生了内存泄漏,可以在该部分之前和之后对内存状态拍快照,然后使用 _CrtMemDifference比较这两个状态:
顾名思义, _CrtMemDifference 比较两个内存状态(s1 和 s2),生成这两个状态之间差异的结果(s3)。 在程序的开始和结尾放置 _CrtMemCheckpoint 调用,并使用 _CrtMemDifference 比较结果,是检查内存泄漏的另一种方法。 如果检测到泄漏,则可以使用 _CrtMemCheckpoint 调用通过二进制搜索技术来划分程序和定位泄漏。 如上面的例子程序我们可以这样来定位确切的调用GetMemory的地方: + View Code 调试时,程序输出如下结果:
这说明在s1和s2之间存在内存泄漏!!!如果GetMemory不是在s1和s2之间调用,那么就不会有信息输出。 3、Linux平台下的内存泄漏检测在上面我们介绍了,vs中在代码中“包含crtdbg.h,将 malloc 和 free 函数映射到它们的调试版本,即 _malloc_dbg 和_free_dbg,这两个函数将跟踪内存分配和释放。 此映射只在调试版本(在其中定义了 _DEBUG)中发生。 发布版本使用普通的 malloc 和 free 函数。”即为malloc和free做了钩子,用于记录内存分配信息。 Linux下面也有原理相同的方法――mtrace,http://en.wikipedia.org/wiki/Mtrace。方法类似,我这就不具体描述,参加给出的链接。这节我主要介绍一个非常强大的工具valgrind。如下图所示:
如上图所示知道: ==6118== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1 是在main中调用了GetMemory导致的内存泄漏,GetMemory中是调用了malloc导致泄漏了100字节的内存。
Valgrind的使用请见手册http://valgrind.org/docs/manual/manual.html。 4、总结其实内存泄漏的原因可以概括为:调用了malloc/new等内存申请的操作,但缺少了对应的free/delete,总之就是,malloc/new比free/delete的数量多。我们在编程时需要注意这点,保证每个malloc都有对应的free,每个new都有对应的deleted!!!平时要养成这样一个好的习惯。 要避免内存泄漏可以总结为以下几点:
以上就是本篇的全部内容了,感谢大家的阅读! (责任编辑:小恩) |