如何找内存泄露
先写一段这样的程序:
#include "stdafx.h"
#include <windows.h>
void AllocateMemory()
{
int* a = new int[2000];
for(int i=0;i<2000;i++)
{
*a=0;
a++;
}
Sleep(1);
}
int _tmain(int argc, _TCHAR* argv[])
{ while(1)
{
AllocateMemory();
}
return 0;
}
然后打开windbg,attach上这个程序(非侵入),在DbgBreakPoint停止
输入命令:!heap -s
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
00150000 00000002 1024 16 16 2 1 1 0 0 L
00250000 00001002 64 24 24 15 1 1 0 0 L
00260000 00008000 64 12 12 10 1 1 0 0
00380000 00001002 64 16 16 1 1 1 0 0 L
003a0000 00001002 130112 73260 73260 33 7 1 0 0 L
-----------------------------------------------------------------------------
然后让它继续运行,输入g
过一会再将它break.
0:001> !heap -s
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
00150000 00000002 1024 16 16 2 1 1 0 0 L
00250000 00001002 64 24 24 15 1 1 0 0 L
00260000 00008000 64 12 12 10 1 1 0 0
00380000 00001002 64 16 16 1 1 1 0 0 L
003a0000 00001002 130112 97020 97020 35 7 1 0 0 L
-----------------------------------------------------------------------------
然后再g,过一会再break.
0:001> !heap -s
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
00150000 00000002 1024 16 16 2 1 1 0 0 L
00250000 00001002 64 24 24 15 1 1 0 0 L
00260000 00008000 64 12 12 10 1 1 0 0
00380000 00001002 64 16 16 1 1 1 0 0 L
003a0000 00001002 261184 179204 179204 35 8 1 0 0 L
-----------------------------------------------------------------------------
注意看003a0000地址,它申请的内存和使用的内存都一直再长。 说明这个堆地址有泄漏。
对这个堆做个统计:
!heap -stat -h 003a0000
heap @ 003a0000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
1f40 597c - aec6300 (99.99)
800 2 - 1000 (0.00)
48a 1 - 48a (0.00)
214 2 - 428 (0.00)
245 1 - 245 (0.00)
220 1 - 220 (0.00)
94 2 - 128 (0.00)
72 2 - e4 (0.00)
c8 1 - c8 (0.00)
54 2 - a8 (0.00)
90 1 - 90 (0.00)
48 2 - 90 (0.00)
8e 1 - 8e (0.00)
88 1 - 88 (0.00)
44 2 - 88 (0.00)
80 1 - 80 (0.00)
2a 3 - 7e (0.00)
76 1 - 76 (0.00)
39 2 - 72 (0.00)
26 3 - 72 (0.00)
发现这个堆中,大小为1f40的内存块最多。
根据大小过滤下这个
!heap -flt s 1f40
_HEAP @ 150000
_HEAP @ 250000
_HEAP @ 260000
_HEAP @ 380000
_HEAP @ 3a0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
003a5c70 03e9 0000 [01] 003a5c78 01f40 - (busy)
003a7bb8 03e9 03e9 [01] 003a7bc0 01f40 - (busy)
003a9b00 03e9 03e9 [01] 003a9b08 01f40 - (busy)
003aba48 03e9 03e9 [01] 003aba50 01f40 - (busy)
003ad990 03e9 03e9 [01] 003ad998 01f40 - (busy)
00410040 03e9 03e9 [01] 00410048 01f40 - (busy)
00411f88 03e9 03e9 [01] 00411f90 01f40 - (busy)
00413ed0 03e9 03e9 [01] 00413ed8 01f40 - (busy)
00415e18 03e9 03e9 [01] 00415e20 01f40 - (busy)
00417d60 03e9 03e9 [01] 00417d68 01f40 - (busy)
00419ca8 03e9 03e9 [01] 00419cb0 01f40 - (busy)
0041bbf0 03e9 03e9 [01] 0041bbf8 01f40 - (busy)
0041db38 03e9 03e9 [01] 0041db40 01f40 - (busy)
0041fa80 03e9 03e9 [01] 0041fa88 01f40 - (busy)
004219c8 03e9 03e9 [01] 004219d0 01f40 - (busy)
00423910 03e9 03e9 [01] 00423918 01f40 - (busy)
00425858 03e9 03e9 [01] 00425860 01f40 - (busy)
004277a0 03e9 03e9 [01] 004277a8 01f40 - (busy)
004296e8 03e9 03e9 [01] 004296f0 01f40 - (busy)
0042b630 03e9 03e9 [01] 0042b638 01f40 - (busy)
... ...
明显可以看到3a0000中有不少这种内存块。取一个UserPtr,调用!heap -p -a UsrPtr
这样可以显示出这个块的调用栈。
0:001> !heap -p -a 0b2fe198
address 0b2fe198 found in
_HEAP @ 3a0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0b2fe190 03e9 0000 [01] 0b2fe198 01f40 - (busy)
这里什么都没有,当然你需要配置操作系统标志来启动跟踪内存泄露进程的user stack。
启动方法就是运行下面指令gflags.exe /i Testleak.exe +ust。
这样运行上面命令就有
!heap -p -a 0143d8c8
address 0143d8c8 found in
_HEAP @ 330000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0143d8c0 03f0 0000 [07] 0143d8c8 01f64 - (busy)
Trace: 0025
7c96d6dc ntdll!RtlDebugAllocateHeap+0x000000e1
7c949d18 ntdll!RtlAllocateHeapSlowly+0x00000044
7c91b298 ntdll!RtlAllocateHeap+0x00000e64
102c103e MSVCR90D!_heap_alloc_base+0x0000005e
102cfd76 MSVCR90D!_heap_alloc_dbg_impl+0x000001f6
102cfb2f MSVCR90D!_nh_malloc_dbg_impl+0x0000001f
102cfadc MSVCR90D!_nh_malloc_dbg+0x0000002c
102db25b MSVCR90D!malloc+0x0000001b
102bd691 MSVCR90D!operator new+0x00000011
102bd71f MSVCR90D!operator new[]+0x0000000f
4113d8 Test2!AllocateMemory+0x00000028
41145c Test2!wmain+0x0000002c
411a08 Test2!__tmainCRTStartup+0x000001a8
41184f Test2!wmainCRTStartup+0x0000000f
7c816fd7 kernel32!BaseProcessStart+0x00000023
这样的效果
这样泄露的位置就很清楚的找到了