一. 问题描述
当一个进程的内存异常大时,可能发生了内存泄漏,也可能是内存中相关队列等数据结构长度未作限制。
二. 解决思路
如果问题很容易出现,最好是使用 umdh 工具来进行判断,此工具的使用方法在其帮助手册中有详细介绍,此处不再赘述。
本文中说的是另外一处思路。众所周知,进程中分为代码空间,数据空间,栈,堆等。除了堆之外,其他部分都是基本固定和有上限限制的。堆的空间大小对于 64 位的进程可以说基本不受限的,因此我们的思路是查看堆中的内容。
三. 解决方法
使用 windbg attach 到进程后,依次执行
0:003> !heap -s
NtGlobalFlag enables following debugging aids for new heaps:
stack back traces
LFH Key : 0x0000008f4b2ee335
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-------------------------------------------------------------------------------------
0000000002460000 08000002 1024 432 1024 1 6 1 0 0 LFH
0000000000010000 08008000 64 8 64 5 1 1 0 0
0000000000020000 08008000 64 64 64 61 1 1 0 0
00000000001a0000 08001002 1088 256 1088 3 1 2 0 0 LFH
00000000040a0000 08001002 512 28 512 0 2 1 0 0
0000000004080000 08001002 1088 332 1088 6 5 2 0 0 LFH
-------------------------------------------------------------------------------------
0:003> !heap -stat -h 0000000004080000
heap @ 0000000004080000
group-by: TOTSIZE max-display: 20
size #blocks total (%) (percent of total busy bytes)
6608 1 - 6608 (36.24)
4000 1 - 4000 (22.73)
1000 2 - 2000 (11.37)
1001 1 - 1001 (5.68)
b00 1 - b00 (3.91)
2c8 3 - 858 (2.96)
200 4 - 800 (2.84)
401 1 - 401 (1.42)
3ff 1 - 3ff (1.42)
30a 1 - 30a (1.08)
178 2 - 2f0 (1.04)
28 11 - 2a8 (0.94)
38 a - 230 (0.78)
220 1 - 220 (0.75)
100 2 - 200 (0.71)
58 5 - 1b8 (0.61)
160 1 - 160 (0.49)
130 1 - 130 (0.42)
30 6 - 120 (0.40)
48 3 - d8 (0.30)
0:003> !heap -flt s 401(这个是随机抽选的,当然我写的示例中正好是泄漏了这么大)_HEAP @ 2460000
_HEAP @ 10000
_HEAP @ 20000
_HEAP @ 1a0000
_HEAP @ 40a0000
_HEAP @ 4080000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0000000003f907e0 0043 0000 [00] 0000000003f90810 00401 - (busy)
0:003> !heap -p -a 0000000003f90810
address 0000000003f90810 found in
_HEAP @ 4080000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0000000003f907e0 0043 0000 [00] 0000000003f90810 00401 - (busy)
7700cc0d ntdll! ?? ::FNODOBFM::`string'+0x000000000001913b
71048d17 MSVCR100!malloc+0x000000000000005b
71048ddb MSVCR100!operator new+0x000000000000001f
13fa423d7 test!test1+0x0000000000000017
13fa47ed4 test!boost::detail::thread_data<long (__cdecl*)(void)>::run+0x0000000000000014
13fa52e43 test!boost::`anonymous namespace'::thread_start_function+0x0000000000000043
71001d9f MSVCR100!_callthreadstartex+0x0000000000000017
71001e3b MSVCR100!_threadstartex+0x000000000000007f
76e6652d kernel32!BaseThreadInitThunk+0x000000000000000d
76f9c521 ntdll!RtlUserThreadStart+0x000000000000001d
这样就可以定位到申请内存的代码行数(需要注意的是,设置符号文件,并且执行 gflags.exe /i *.exe +ust)
四. 后记
1. 有时候我们可能没有来得及设置 gflags,这时可以查看堆中的内存内容来猜测可能泄漏的地方。
2.heap - s 可以执行多次,观测内存增加的堆地址进行分析。