VC中检测内存泄露

1. 使用VC自带的crtdbg

在需要检测的函数所在文件最前面定义

#include <crtdbg.h>

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

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

然后,在需要检测的函数的起始处调用EnableMemLeakCheck(); 比如:

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

运行(提醒:不要按Ctrl+F5,按F5),当程序运行完成后,就可以显示出是否有内存泄露。

定位内存泄露位置的方法:

上面所生成的内存泄露报告大概如下:

Detected memory leaks!
Dumping objects ->
c:\work\test.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} 代表了第52次内存分配操作发生了泄漏。可以调用crtdbg提供的: long _CrtSetBreakAlloc(long nAllocID),让程序在52次内存分配操作的时候,自动停下来,进入调试状态。使用如下所示:

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

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

2. WinDbg检测内存泄露

1 设置参数

     运行Global Flags程序, 在System Register属性页下勾选中“Enable pool tagging”和“Enable heap tagging”两项,在Kernel Flags下勾选“Enable heap tagging”。

2 设置程序命令参数

    打开cmd,将当前目录修改为gflags.exe目录(如 cd C:\Program Files\Debugging Tools for Windows (x86)), 然后输入gflags.exe /i  (程序名) +ust 即可

    如果设置失败,说明注册表被禁用了,可以尝试解除所有对注册表的禁用。这个注册表位置为:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options,命令“gflags.exe /i test.exe +ust”实际上就是在该路径下创建一个子键“test.exe”并创建一个名为GlobalFlag内容为0x00001000的REG_DWORD值。

    参考:http://support.citrix.com/article/CTX106970

    我起初的时候也设置失败了,后来发现是360的一个服务禁止了对上述注册表的操作,卸载之后就行了。

3 设置符号库

    如果符号不全或者不正确,也不能使用该方法侦测内存泄露的位置。

4 调试程序检测内存泄露

   运行WinDbg程序,找到菜单File下的Open Executable,打开要调试的程序,F5就可调试。以下为一些常用命令

   .symfix C:\Test      // 设置符号文件路径

   .reload     // 加载符号文件

   !heap -x  内存地址   // 打印出相应的堆栈目录

   !heap -p -a 内存地址(Entry地址) // 打印出详细的堆栈信息

5 实例

Detected memory leaks!
Dumping objects ->
e:\vs工程\tests\testsdlg.cpp(101) : {118} normal block at 0x003BBAD8, 100 bytes long.
Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

!heap -x 0x003BBAD8

Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
003bbab0  003bbab8  003b0000  003b0640        a0      17f0        18  busy extra fill 

!heap -p -a 003bbab0  

address 003bbab0 found in
_HEAP @ 3b0000
HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
003bbab0 0014 0000  [07]   003bbab8    00088 - (busy)
Trace: 0357
7c98eed2 ntdll!RtlDebugAllocateHeap+0x000000e1
7c96b394 ntdll!RtlAllocateHeapSlowly+0x00000044
7c938f21 ntdll!RtlAllocateHeap+0x00000e64
1024db9c MSVCR80D!malloc_base+0x000000ec
1020faa5 MSVCR80D!malloc_dbg+0x000002d5
1020f839 MSVCR80D!malloc_dbg+0x00000069
1020f7ef MSVCR80D!malloc_dbg+0x0000001f
78332934 MFC80UD+0x00052934
783329b8 MFC80UD+0x000529b8
78332396 MFC80UD+0x00052396
4129ae tests!CtestsDlg::OnInitDialog+0x0000013e
7839bfae MFC80UD+0x000bbfae
77d18734 USER32!InternalCallWinProc+0x00000028
77d2413c USER32!UserCallDlgProcCheckWow+0x000000f0
77d23b30 USER32!DefDlgProcWorker+0x000000a8
77d23d5c USER32!DefDlgProcW+0x00000022
77d18734 USER32!InternalCallWinProc+0x00000028
77d18816 USER32!UserCallWinProcCheckWow+0x00000150
77d2a013 USER32!CallWindowProcAorW+0x00000098
77d2a039 USER32!CallWindowProcW+0x0000001b
7835e302 MFC80UD+0x0007e302
7835cb1b MFC80UD+0x0007cb1b
7839d393 MFC80UD+0x000bd393
7835fbf7 MFC80UD+0x0007fbf7
7835f3b0 MFC80UD+0x0007f3b0
7835c9be MFC80UD+0x0007c9be
7835ceb4 MFC80UD+0x0007ceb4
78358979 MFC80UD+0x00078979
77d18734 USER32!InternalCallWinProc+0x00000028
77d18816 USER32!UserCallWinProcCheckWow+0x00000150
77d2927b USER32!SendMessageWorker+0x000004a5
77d2651a USER32!InternalCreateDialog+0x000009df

其中4129ae 地址就是分配内存的地方,从而导致的内存泄露。

3. 参考

1. 最快速度找到内存泄漏

2. WinDbg的调试方法

 

posted @ 2013-01-08 10:33  金石开  阅读(496)  评论(0编辑  收藏  举报