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. 参考