VC的C运行时库(C Run-Time Libraries, CRT库)中集成了非常完善的内存泄露调试功能,最近在使用此功能时发现网上的中文资料支离破碎,而且有些资料中还存在错误,为了方便大家查阅,特地整理出来放在这里。
本文内容来自 https://msdn.microsoft.com/en-us/library/x98tx3cf.aspx,按照自己的理解和验证情况重新做了整理,如果有和原文内容不符合的,以原始文章为准。
1、输出内存使用信息
首先要保证代码是在DEBUG模式下(定义有_DEBUG宏)编译和运行,在退出程序之前,调用 _CrtDumpMemoryLeaks(); 输出内存使用信息,如果存在内存泄露,会输出和泄露内存有关的数据,输出信息类似:
Detectedmemory leaks!
Dumpingobjects ->
{53}normal block at 0x00601558, 4 bytes long.
Data: < > CD CD CD CD
程序中可能有多个退出的位置,逐个做上述调用太麻烦而且容易出现遗漏,我们可以在程序起始的位置调用 _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 上述语句的效果是自动在程序退出之前调用 _CrtDumpMemoryLeaks(); ,相当于在所有的程序出口添加了上面的调用。
2、输出泄露内存的申请位置
虽然确定了程序中存在内存泄露,而且可以知道泄露内存的长度及存储的数据信息,但是要定位具体的泄露位置还是有困难的,尤其是对于大型软件系统更是如此。如果能够定位到内存是在哪里申请的,可以大大方便我们确定产生内存泄露的原因。
对于C接口malloc申请的内存,在代码文件中包含下面的语句:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
注意:要严格按照上面的次序#include头文件,否则CRT库可能工作不正常。
这次我们得到的内存泄露信息包含了泄露内存申请的位置,类似:
Detectedmemory leaks!
Dumpingobjects ->
c:\test\test.cpp(21): {53} normal block at 0x00831558, 4 bytes long.
Data: < > CD CD CD CD
但是我们发现,上面的手段对C++中new操作申请的内存是无效的,如果要对new申请的内存也能够输出申请位置的信息,方法是在代码文件中包含下面的语句:
#ifdef_DEBUG
#ifndefDBG_NEW
#defineDBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define newDBG_NEW
#endif
#endif
3、根据内存申请序号设置断点
上面的每个内存泄露信息中有用大括号包起来的内存申请序号 {53},表明这是程序申请的第53块内存,如果每次泄露的内存序号都是固定的,我们可以在申请该内存时设置断点,以进一步便于定位泄露原因。具体的步骤是:
(1) 在程序的起始位置设置断点,具体内容不限,保证在出问题的内存泄露之前即可
(2) 以调试方式执行程序,使程序停止在断点位置
(3) 在菜单debug / windows / watch打开任一watch窗口
在watch窗口的name栏,输入_crtBreakAlloc ;如果使用使用多线程DLL版本,原文说,需要输入 {,,ucrtbased.dll}_crtBreakAlloc,这个方法至少对vs2010是不通的,应改为 {,,msvcr100d.dll}_crtBreakAlloc,如果是其他版本的vc,相应的dll文件名也需要修改(这里介绍一个确定dll文件名的方法,调试模式下在debug窗口会有输出类似于 Loaded 'C:\Windows\System32\msvcr100d.dll',Symbols loaded. ,通过这个提示尝试可行的文件名);
(4) 在value栏中填入内存申请序号
(5) 继续以debug方式运行程序,在申请指定序号的内存时,会自动中断执行
除了手动在watch窗口中设置断点,我们还可以在代码中添加下面的语句来达到同样的目的:
_crtBreakAlloc = 52; 或者
_CrtSetBreakAlloc(52);
4、统计和比较内存状态
可以通过下面的调用,在程序的任何位置生成当前内存使用情况的快照,
_CrtMemState s1;
_CrtMemCheckpoint( &s1 );
执行 _CrtMemDumpStatistics( &s1 ); 可以输出快照信息,如下:
0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
3071 bytes in 16 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 3071 bytes.
Total allocations: 3764 bytes.
如果我们怀疑在某段代码中存在内存泄露,可以在这段代码之前和之后分别生成内存快照,并比较两个内存快照的差异,方法是:
if ( _CrtMemDifference( &s3, &s1, &s2) )
_CrtMemDumpStatistics( &s3 );
输出的信息类似:
0 bytesin 0 Free Blocks.
8 bytesin 2 Normal Blocks.
0 bytesin 0 CRT Blocks.
0 bytesin 0 Ignore Blocks.
0 bytesin 0 Client Blocks.
Largestnumber used: 0 bytes.
Totalallocations: 8 bytes.
可以看出,在两个快照相差了2个内存块共8个字节,此信息有助于分析产生内存泄露的原因。