关键字:浅谈MFC内存泄露检测及内存越界访问保护机制
本文所有代码均在vc2008下编译、调试。如果您使用的编译器不同,结果可能会有差别,但本文讲述的原理对于大部分编译器应该是相似的。对于本文的标题,实在不知道用什么表示更恰当,因为本文不仅淡了内存泄露检测机制,也谈到了指针越界的检测机制。到底应该说是MFC的机制,还是C++的机制?Anyway,相信你看了一定会有所收获。并欢迎常来本博客http://lionel.bokee.com留言讨论。 在我们开发MFC应用程序的时候,不知大家是否注意到Debug版本输出窗口经常会有下面这样的信息:
Detected memory leaks! Dumping objects -> c:/my.data/my.codes/memleak/memleak/memleak.cpp(34) : {126} normal block at 0x00A321A0, 4 bytes long. Data: < > 01 00 00 00 Object dump complete.
编译器是怎么知道我们写的代码有内存泄露并能精确到文件、行号的呢?事实上也并不是所有情况都能精确到文件、行号,看看下面这种情况:
Detected memory leaks! Dumping objects -> First-chance exception at 0x75c739e5 (kernel32.dll) in MemLeak.exe: 0xC0000005: Access violation reading location 0x711af9f4. #File Error#(62) : {137} normal block at 0x00A721A0, 4 bytes long. Data: < > CD CD CD CD Object dump complete.
虽然检测出了内存泄露,但我们只能知道内存地址、行号,文件名是#File Error#,而且还伴随着内存非法访问的异常。这个异常看似是MFC在检测内存泄露的时候产生的。 下面我们从C++内存分配与回收的两个操作符new, delete一步步分析C++内存管理以及MFC内存泄露检测机制。所有这些都是针对Debug版本的,最后我们再看看Release版本的情况。
一、内存分配操作符new 新建一个MFC应用程序,无论是Win32 Console Application + MFC Support,还是MFC Application或者是MFC DLL。编译器为我们生成的代码最前面,在#include下面都会有下面这三行代码:
#ifdef _DEBUG #define new DEBUG_NEW #endif
这三句话的意思是,如果是Debug版本,那么将new操作符定义为DEBUG_NEW。在afx.h中有对DEBUG_NEW的定义:
// Memory tracking allocationvoid* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine); #define DEBUG_NEW new(THIS_FILE, __LINE__)
看来MFC是重新定义了一个new操作符,并把文件名、行号调试信息传给了new。下面是这个new操作符调用的其它函数。可见是按照MFC -> C++ -> C -> Win32 API的流程分配