内存泄露检测基本原理:
程序调用new、malloc、realloc等内存分配函数时,将分配的内存地址,分配的大小、第几次分配、分配函数调用所在的文件名、所在的行数等这些信息记录到一个链表中。程序调用delete、free等内存释放函数时,通过释放的内存地址,查找链表,如果找到就将该内存地址对应的信息从链表中删除(如果未找到说明这个内存地址可能之前已经释放过了,或者根本就没分配过,可能存在潜在的问题)。程序退出的时候,如果链表还有数据,说明存在内存泄露,输出链表中的数据。
MS CRT已支持内存泄露的检测,使用MFC向导生成的工程,在DEBUG模式下,默认会开启内存泄露检测,程序退出时,如果有内存泄露,泄露的内存信息将在输出窗口中显示。
常见问题:
1. 为什么输出窗口有检测到内存泄露,但是没有显示文件名及第几行的信息?
要检测内存泄露,首先要跟踪内存分配函数,所以要将new、malloc等内存函数接管过来。默认内存分配函数没有文件名和所在行的信息,于是CRT宏定义了malloc、realloc,如下:
#define
#define
#define
这样源代码中的所有malloc还有realloc,就都被替换为对应的有文件名和所在行的内存分配函数了。
这些宏定义生效需要定义_CRTDBG_MAP_ALLOC宏,可以在 项目-》C/C++-》预处理 中定义。
同理对于new,也要宏定义,将默认的new重定向到包含有文件名和行号的版本new(__FILE__, __LINE__)。
可以将如下一段代码保存到SetDebugNew.h文件中,
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK
#define new DEBUG_CLIENTBLOCK
#endif
在源代码文件中包含该头文件,如果有stdafx.h文件,在文件中#include "SetDebugNew.h",这样每个.c或.cpp文件就都使用debug的new版本。
2. 已经按照问题1的解决方案进行修订,但还是有内存泄露没有文件名和代码行号?
Detected memory leaks!
Dumping objects ->
d:\local\project\test.cpp(278) : {354} normal block at 0x003FDAB8, 44 bytes long.
Object dump complete.
如果内存块号比较稳定,可以调用_CrtSetBreakAlloc,将块号作为参数设置一个断点。程序分配这个内存时将break,再查看调用堆栈。
3. 为什么程序退出时没有进行内存泄露检测?
#ifdef _DEBUG
#endif
4. 为什么在DLL中定义一个static对象,该对象在析构时会自动释放内存,但还是提示内存泄露?