使用_CRTDBG_LEAK_CHECK_DF检查VC程序的内存泄漏(转)

咱们知道,MFC程序若是检测到存在内存泄漏,退出程序的时候会在调试窗口提醒内存泄漏。例如:html

class CMyApp : public CWinApp
{
public:
BOOL InitApplication()
{
int* leak = new int[10];
return TRUE;
}
};
产生的内存泄漏报告大致以下:程序员

Detected memory leaks!
Dumping objects ->
c:worktest.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
这挺好。问题是,若是咱们不喜欢MFC,那么难道就没有办法?或者本身作?多线程

呵呵,这不须要。其实,MFC也没有本身作。内存泄漏检测的工做是VC++的C运行库作的。也就是说,只要你是VC++程序员,均可以很方便地检测内存泄漏。咱们仍是给个样例:函数

#includepost

inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}线程

void main()
{
EnableMemLeakCheck();
int* leak = new int[10];
}
运行(提醒:不要按Ctrl+F5,按F5),你将发现,产生的内存泄漏报告与MFC相似,但有细节不一样,以下:调试

Detected memory leaks!
Dumping objects ->
{52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
为何呢?看下面。orm

定位内存泄漏因为哪一句话引发的
你已经发现程序存在内存泄漏。如今的问题是,咱们要找泄漏的根源。htm

通常咱们首先肯定内存泄漏是因为哪一句引发。在MFC中,这一点很容易。你双击内存泄漏报告的文字,或者在Debug窗口中按F4,IDE就帮你定位到申请该内存块的地方。对于上例,也就是这一句:对象

int* leak = new int[10];

这多多少少对你分析内存泄漏有点帮助。特别地,若是这个new仅对应一条delete(或者你把delete漏写),这将很快能够确认问题的症结。

咱们前面已经看到,不使用MFC的时候,生成的内存泄漏报告与MFC不一样,并且你马上发现按F4不灵。那么难道MFC作了什么手脚?

其实不是,咱们来模拟下MFC作的事情。看下例:

inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}

#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void main()
{
EnableMemLeakCheck();
int* leak = new int[10];
}

再运行这个样例,你惊喜地发现,如今内存泄漏报告和MFC没有任何分别了。

快速找到内存泄漏
单肯定了内存泄漏发生在哪一行,有时候并不足够。特别是同一个new对应有多处释放的情形。在实际的工程中,如下两种状况很典型:

建立对象的地方是一个类工厂(ClassFactory)模式。不少甚至所有类实例由同一个new建立。对于此,定位到了new出对象的所在行基本没有多大帮助。

COM对象。咱们知道COM对象采用Reference Count维护生命周期。也就是说,对象new的地方只有一个,可是Release的地方不少,你要一个个排除。 
那么,有什么好办法,能够迅速定位内存泄漏?

答:有。

在内存泄漏状况复杂的时候,你能够用如下方法定位内存泄漏。这是我我的认为通用的内存泄漏追踪方法中最有效的手段。

咱们再回头看看crtdbg生成的内存泄漏报告:

Detected memory leaks!
Dumping objects ->
c:worktest.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete. 
除了产生该内存泄漏的内存分配语句所在的文件名、行号为,咱们注意到有一个比较陌生的信息:。这个整数值表明了什么意思呢?

其实,它表明了第几回内存分配操做。象这个例子,表明了第52次内存分配操做发生了泄漏。你可能要说,我只new过一次,怎么会是第52次?这很容易理解,其余的内存申请操做在C的初始化过程调用的呗。:)

有没有可能,咱们让程序运行到第52次内存分配操做的时候,自动停下来,进入调试状态?所幸,crtdbg确实提供了这样的函数:即 long _CrtSetBreakAlloc(long nAllocID)。咱们加上它:

inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}

#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void main()
{
EnableMemLeakCheck();
_CrtSetBreakAlloc(52);
int* leak = new int[10];
}
你发现,程序运行到 int* leak = new int[10]; 一句时,自动停下来进入调试状态。细细体会一下,你能够发现,这种方式你得到的信息远比在程序退出时得到文件名及行号有价值得多。由于报告泄漏文件名及行号,你得到的只是静态的信息,然而_CrtSetBreakAlloc则是把整个现场恢复,你能够经过对函数调用栈分析(我发现不少人不习惯看函数调用栈,若是你属于这种状况,我强烈推荐你去补上这一课,由于它过重要了)以及其余在线调试技巧,来分析产生内存泄漏的缘由。一般状况下,这种分析方法能够在5分钟内找到肇事者。

固然,_CrtSetBreakAlloc要求你的程序执行过程是可还原的(屡次执行过程的内存分配顺序不会发生变化)。这个假设在多数状况下成立。不过,在多线程的状况下,这一点有时难以保证。

 

原文转载自:http://www.cnblogs.com/qq78292959/archive/2011/05/28/2076605.html

相关文章
相关标签/搜索