C的内存泄漏检测

一,Windows平台下的内存泄漏检测

检测是否存在内存泄漏问题

Windows平台下面Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法,原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏。在vs中启用内存检测的方法如下:

•STEP1,在程序中包括以下语句: (#include 语句必须采用上文所示顺序。 如果更改了顺序,所使用的函数可能无法正常工作。)

#define _CRTDBG_MAP_ALLOC //将 CRT 堆函数的基版本映射到对应的“Debug”版本。 并非绝对需要该语句;但如果没有该语句,内存泄漏转储包含的有用信息将较少。
#include <stdlib.h>
#include <crtdbg.h> //将 malloc 和 free 函数映射到它们的调试版本,即 _malloc_dbg 和 _free_dbg,这两个函数将跟踪内存分配和释放。 此映射只在调试版本(在其中定义了_DEBUG)中发生。 发布版本使用普通的 malloc和 free函数。

•STEP2, 在添加了上述语句之后,可以通过在程序中包括以下语句(通常应恰好放在程序退出位置之前)来转储内存泄漏信息:

_CrtDumpMemoryLeaks();

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.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;
    _CrtDumpMemoryLeaks();
    return 0;
}

未定义 _CRTDBG_MAP_ALLOC 时,所显示的会是:

C的内存泄漏检测

  • 内存分配编号(在大括号内)。
  • 块类型(普通、客户端或 CRT)。
    • “普通块”是由程序分配的普通内存。
    • “客户端块”是由 MFC 程序用于需要析构函数的对象的特殊类型内存块。 MFC new 操作根据正在创建的对象的需要创建普通块或客户端块。
    • “CRT 块”是由 CRT 库为自己使用而分配的内存块。 CRT 库处理这些块的释放,因此您不大可能在内存泄漏报告中看到这些块,除非出现严重错误(例如 CRT 库损坏)。
    • “可用块”是已释放的内存块。不会出现在内存泄漏报告中。
    • “忽略块”是您已特别标记的块,因而不出现在内存泄漏报告中。
  • 十六进制形式的内存位置。
  • 以字节为单位的块大小。
  • 十六进制形式的前 16 字节内容。

定义了 _CRTDBG_MAP_ALLOC 时,还会显示在其中分配泄漏的内存的文件。 文件名后括号中的数字(本示例中为 10)是该文件中的行号。

C的内存泄漏检测

注意:如果程序总是在同一位置退出,调用 _CrtDumpMemoryLeaks 将非常容易。 如果程序从多个位置退出,则无需在每个可能退出的位置放置对_CrtDumpMemoryLeaks 的调用,而可以在程序开始处包含以下调用:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

该语句在程序退出时自动调用 _CrtDumpMemoryLeaks。

定位具体的内存泄漏地方

通过上面的方法,我们几乎可以定位到是哪个地方调用内存分配函数malloc和new等,如上例中的GetMemory函数中,即第10行!但是不能定位到,在哪个地方调用GetMemory()导致的内存泄漏,而且在大型项目中可能有很多处调用GetMemory。

一种定位内存泄漏的技术是对内存状态拍快照。若要确定代码中某一部分是否发生了内存泄漏,可以在该部分之前和之后对内存状态拍快照,然后使用 _CrtMemDifference 比较这两个状态,比如,在程序的开始和结尾放置 _CrtMemCheckpoint 调用,并使用_CrtMemDifference 比较结果, 如果检测到泄漏,则可以使用 _CrtMemCheckpoint 调用通过二进制搜索技术来划分程序和定位泄漏。

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
 
#include <iostream>
using namespace std;
 
_CrtMemState s1, s2, s3; //_CrtMemState结构用来存储内存状态的快照
 
void GetMemory(char *p, int num)
{
    p = (char*)malloc(sizeof(char) * num);
}
 
int main(int argc,char** argv)
{
    _CrtMemCheckpoint( &s1 ); //_CrtMemCheckpoint()用来拍内存快照
    char *str = NULL;
    GetMemory(str, 100);
    _CrtMemCheckpoint( &s2 );
    if ( _CrtMemDifference( &s3, &s1, &s2) ) //CrtMemDifference 比较两个内存状态(s1 和 s2),生成这两个状态之间差异的结果(s3)
        _CrtMemDumpStatistics( &s3 ); //dump s3内容
    cout<<"Memory leak test!"<<endl;
    _CrtDumpMemoryLeaks();
    return 0;
}

输出结果如下

C的内存泄漏检测

 二,Linux平台下的内存泄漏检测

vs的方法是为malloc和free做了钩子,用于记录内存分配信息。

Linux下面也有原理相同的方法——mtrace,http://en.wikipedia.org/wiki/Mtrace。

另一种方法是使用Valgrind工具 http://valgrind.org/docs/manual/manual.html

最后要说,我们在编程时需要注意保证每个malloc都有对应的free,每个new都有对应的deleted!!!