托管应用程序中的非托管内存泄漏
本文关键字:内存 泄漏 应用程序 | 更新日期: 2024-11-08 21:48:44
我正在尝试找到非托管堆的根本原因。我的应用程序是一个 .NET 应用程序,但它调用了一些C++的第三方库。我拍摄了两个相隔1.5GB的内存快照。
这是来自第一个快照的 !address 摘要的输出
0:000> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 1144 7fa`6695c000 ( 7.978 Tb) 99.73%
<unknown> 1498 4`4ba12000 ( 17.182 Gb) 76.71% 0.21%
Heap 1507 1`13c3c000 ( 4.309 Gb) 19.24% 0.05%
Stack 2385 0`31540000 ( 789.250 Mb) 3.44% 0.01%
Image 1289 0`0831a000 ( 131.102 Mb) 0.57% 0.00%
TEB 794 0`00634000 ( 6.203 Mb) 0.03% 0.00%
Other 14 0`001b7000 ( 1.715 Mb) 0.01% 0.00%
PEB 1 0`00001000 ( 4.000 kb) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE 5613 5`8adfb000 ( 22.170 Gb) 98.99% 0.27%
MEM_IMAGE 1822 0`0bbfa000 ( 187.977 Mb) 0.82% 0.00%
MEM_MAPPED 53 0`02c9f000 ( 44.621 Mb) 0.19% 0.00%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE 1144 7fa`6695c000 ( 7.978 Tb) 99.73%
MEM_RESERVE 2121 4`07c5c000 ( 16.121 Gb) 71.98% 0.20%
MEM_COMMIT 5367 1`91a38000 ( 6.276 Gb) 28.02% 0.08%
--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE 3216 1`831df000 ( 6.049 Gb) 27.01% 0.07%
PAGE_EXECUTE_READ 283 0`095c5000 ( 149.770 Mb) 0.65% 0.00%
PAGE_READONLY 846 0`03242000 ( 50.258 Mb) 0.22% 0.00%
PAGE_EXECUTE_READWRITE 143 0`00f9b000 ( 15.605 Mb) 0.07% 0.00%
PAGE_READWRITE|PAGE_GUARD 795 0`00f2b000 ( 15.168 Mb) 0.07% 0.00%
PAGE_WRITECOPY 83 0`00188000 ( 1.531 Mb) 0.01% 0.00%
PAGE_EXECUTE 1 0`00004000 ( 16.000 kb) 0.00% 0.00%
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free 5`c0090000 7f8`da150000 ( 7.972 Tb)
<unknown> 3`9c0ee000 0`e3fa2000 ( 3.562 Gb)
Heap 0`09a90000 0`00fd0000 ( 15.813 Mb)
Stack 0`00cf0000 0`000fc000 (1008.000 kb)
Image 7fe`fe0aa000 0`0089e000 ( 8.617 Mb)
TEB 7ff`ff7bc000 0`00002000 ( 8.000 kb)
Other 0`007f0000 0`00181000 ( 1.504 Mb)
PEB 7ff`fffd3000 0`00001000 ( 4.000 kb)
这是第二个快照中 !address -summary 的输出
0:000> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 1129 7fa`2cad7000 ( 7.977 Tb) 99.72%
<unknown> 1520 4`4ca9a000 ( 17.198 Gb) 73.80% 0.21%
Heap 1585 1`486b3000 ( 5.132 Gb) 22.02% 0.06%
Stack 2586 0`35840000 ( 856.250 Mb) 3.59% 0.01%
Image 1275 0`0831a000 ( 131.102 Mb) 0.55% 0.00%
TEB 861 0`006ba000 ( 6.727 Mb) 0.03% 0.00%
Other 14 0`001b7000 ( 1.715 Mb) 0.01% 0.00%
PEB 1 0`00001000 ( 4.000 kb) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE 5978 5`c3c7d000 ( 23.059 Gb) 98.96% 0.28%
MEM_IMAGE 1810 0`0bbfa000 ( 187.977 Mb) 0.79% 0.00%
MEM_MAPPED 54 0`03ca2000 ( 60.633 Mb) 0.25% 0.00%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE 1129 7fa`2cad7000 ( 7.977 Tb) 99.72%
MEM_RESERVE 2218 3`ea250000 ( 15.659 Gb) 67.20% 0.19%
MEM_COMMIT 5624 1`e92c9000 ( 7.643 Gb) 32.80% 0.09%
--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE 3419 1`da924000 ( 7.415 Gb) 31.82% 0.09%
PAGE_EXECUTE_READ 283 0`095c5000 ( 149.770 Mb) 0.63% 0.00%
PAGE_READONLY 846 0`03242000 ( 50.258 Mb) 0.21% 0.00%
PAGE_READWRITE|PAGE_GUARD 862 0`01071000 ( 16.441 Mb) 0.07% 0.00%
PAGE_EXECUTE_READWRITE 148 0`00fec000 ( 15.922 Mb) 0.07% 0.00%
PAGE_WRITECOPY 65 0`0013d000 ( 1.238 Mb) 0.01% 0.00%
PAGE_EXECUTE 1 0`00004000 ( 16.000 kb) 0.00% 0.00%
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free 5`e2b3f000 7f8`b76a1000 ( 7.972 Tb)
<unknown> 1`a5991000 0`da6ff000 ( 3.413 Gb)
Heap 0`09a90000 0`00fd0000 ( 15.813 Mb)
Stack 0`00cf0000 0`000fc000 (1008.000 kb)
Image 7fe`fe0aa000 0`0089e000 ( 8.617 Mb)
TEB 7ff`ff7bc000 0`00002000 ( 8.000 kb)
Other 0`007f0000 0`00181000 ( 1.504 Mb)
PEB 7ff`fffd3000 0`00001000 ( 4.000 kb)
Basedo 这些,看起来本机堆增长了 1GB。因此,让我们看一下本机堆。这是第一个快照
0:000> !heap -s
LFH Key : 0x0000007b0b4a5afd
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-------------------------------------------------------------------------------------
Virtual block: 00000000031d0000 - 00000000031d0000 (size 0000000000000000)
Virtual block: 0000000003690000 - 0000000003690000 (size 0000000000000000)
Virtual block: 0000000024bf0000 - 0000000024bf0000 (size 0000000000000000)
Virtual block: 00000001412c0000 - 00000001412c0000 (size 0000000000000000)
Virtual block: 0000000158120000 - 0000000158120000 (size 0000000000000000)
Virtual block: 0000000102bd0000 - 0000000102bd0000 (size 0000000000000000)
Virtual block: 0000000021a50000 - 0000000021a50000 (size 0000000000000000)
Virtual block: 000000011b230000 - 000000011b230000 (size 0000000000000000)
Virtual block: 0000000073f40000 - 0000000073f40000 (size 0000000000000000)
0000000000090000 00000002 695936 693228 695936 2304 2251 47 9 e6 LFH
0000000000010000 00008000 64 4 64 1 1 1 0 0
0000000000410000 00001002 1088 952 1088 7 4 2 0 0 LFH
0000000000550000 00041002 512 8 512 3 1 1 0 0
0000000000020000 00001002 1088 1044 1088 10 5 2 0 0 LFH
00000000005e0000 00041002 512 256 512 2 2 1 0 0 LFH
0000000000b40000 00001002 1536 680 1536 4 22 2 0 0 LFH
0000000000c70000 00041002 512 8 512 3 1 1 0 0
0000000000a80000 00001002 512 8 512 3 1 1 0 0
0000000000530000 00001002 64 24 64 8 2 1 0 0
0000000001100000 00001002 355456 190148 355456 96748 658 89 0 0 LFH
External fragmentation 50 % (658 free blocks)
0000000001550000 00001002 15424 7084 15424 2921 103 7 0 2 LFH
External fragmentation 41 % (103 free blocks)
0000000005c30000 00001002 1536 608 1536 6 31 2 0 0 LFH
0000000003d00000 00001002 1536 592 1536 4 9 2 0 0 LFH
0000000003df0000 00001002 64 8 64 3 1 1 0 0
0000000003ff0000 00001002 64 8 64 3 1 1 0 0
0000000005e90000 00011002 512 108 512 20 12 1 0 1
0000000005f20000 00001002 512 8 512 3 2 1 0 0
0000000008bc0000 00001002 695936 685784 695936 410 701 47 0 5 LFH
Virtual block: 00000000116e0000 - 00000000116e0000 (size 0000000000000000)
Virtual block: 000000001c7c0000 - 000000001c7c0000 (size 0000000000000000)
0000000006580000 00001002 2703360 2700036 2703360 122093 8251 176 2 1222d LFH
0000000014730000 00001002 1088 328 1088 67 7 2 0 0 LFH
000000004fe50000 00001002 512 8 512 3 1 1 0 0
000000004fdb0000 00001002 512 8 512 3 1 1 0 0
0000000010170000 00001002 512 12 512 1 2 1 0 0
0000000058960000 00001002 512 8 512 1 3 1 0 0
00000000104e0000 00001002 512 264 512 3 5 1 0 0 LFH
-------------------------------------------------------------------------------------
这是第二个快照
0:000> !heap -s
LFH Key : 0x0000007b0b4a5afd
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-------------------------------------------------------------------------------------
Virtual block: 00000000031d0000 - 00000000031d0000 (size 0000000000000000)
Virtual block: 0000000003690000 - 0000000003690000 (size 0000000000000000)
Virtual block: 0000000024bf0000 - 0000000024bf0000 (size 0000000000000000)
Virtual block: 0000000158120000 - 0000000158120000 (size 0000000000000000)
Virtual block: 000000011b230000 - 000000011b230000 (size 0000000000000000)
Virtual block: 000000002d9c0000 - 000000002d9c0000 (size 0000000000000000)
Virtual block: 0000000082f60000 - 0000000082f60000 (size 0000000000000000)
Virtual block: 00000000273a0000 - 00000000273a0000 (size 0000000000000000)
Virtual block: 0000000073e90000 - 0000000073e90000 (size 0000000000000000)
0000000000090000 00000002 857856 847980 857856 3491 2443 57 9 145 LFH
0000000000010000 00008000 64 4 64 1 1 1 0 0
0000000000410000 00001002 1088 952 1088 7 4 2 0 0 LFH
0000000000550000 00041002 512 8 512 3 1 1 0 0
0000000000020000 00001002 1088 1044 1088 10 5 2 0 0 LFH
00000000005e0000 00041002 512 256 512 2 2 1 0 0 LFH
0000000000b40000 00001002 1536 780 1536 7 26 2 0 0 LFH
0000000000c70000 00041002 512 8 512 3 1 1 0 0
0000000000a80000 00001002 512 8 512 3 1 1 0 0
0000000000530000 00001002 64 24 64 8 2 1 0 0
0000000001100000 00001002 355456 190148 355456 96735 658 89 0 0 LFH
External fragmentation 50 % (658 free blocks)
0000000001550000 00001002 15424 7084 15424 2915 97 7 0 2 LFH
External fragmentation 41 % (97 free blocks)
0000000005c30000 00001002 1536 616 1536 9 33 2 0 0 LFH
0000000003d00000 00001002 1536 592 1536 4 9 2 0 0 LFH
0000000003df0000 00001002 64 8 64 3 1 1 0 0
0000000003ff0000 00001002 64 8 64 3 1 1 0 0
0000000005e90000 00011002 512 108 512 20 13 1 0 9
0000000005f20000 00001002 512 8 512 3 2 1 0 0
0000000008bc0000 00001002 857856 852992 857856 426 745 57 0 5 LFH
Virtual block: 00000000116e0000 - 00000000116e0000 (size 0000000000000000)
Virtual block: 00000000124b0000 - 00000000124b0000 (size 0000000000000000)
Virtual block: 000000011bd10000 - 000000011bd10000 (size 0000000000000000)
0000000006580000 00001002 3237696 3221472 3237696 150399 9556 209 3 1b6e4 LFH
0000000014730000 00001002 1088 328 1088 67 7 2 0 0 LFH
000000004fe50000 00001002 512 8 512 3 1 1 0 0
000000004fdb0000 00001002 512 8 512 3 1 1 0 0
0000000010170000 00001002 512 12 512 1 2 1 0 0
0000000058960000 00001002 512 8 512 1 3 1 0 0
0000000010430000 00001002 512 264 512 3 5 1 0 0 LFH
-------------------------------------------------------------------------------------
如果我正确读取此数据,则堆@ 0000000006580000的内存大小增加最多。让我们仔细看看这个堆
再次用于第一个快照
0:000> !heap -stat -h 0000000006580000
heap @ 0000000006580000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
2c48 1 - 2c48 (26.79)
280 c - 1e00 (18.15)
c0 22 - 1980 (15.43)
38 40 - e00 (8.47)
30 40 - c00 (7.26)
40 19 - 640 (3.78)
18 32 - 4b0 (2.84)
20 25 - 4a0 (2.80)
428 1 - 428 (2.51)
12 2d - 32a (1.91)
328 1 - 328 (1.91)
8 58 - 2c0 (1.66)
c 2d - 21c (1.28)
d8 2 - 1b0 (1.02)
1c a - 118 (0.66)
3e 4 - f8 (0.59)
3c 4 - f0 (0.57)
d0 1 - d0 (0.49)
bc 1 - bc (0.44)
b8 1 - b8 (0.43)
这是第二个快照的外观
heap @ 0000000006580000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
2c48 1 - 2c48 (26.80)
280 c - 1e00 (18.16)
c0 22 - 1980 (15.44)
38 40 - e00 (8.47)
30 40 - c00 (7.26)
40 19 - 640 (3.78)
18 32 - 4b0 (2.84)
20 25 - 4a0 (2.80)
428 1 - 428 (2.52)
328 1 - 328 (1.91)
12 2c - 318 (1.87)
8 58 - 2c0 (1.66)
c 2d - 21c (1.28)
d8 2 - 1b0 (1.02)
1c a - 118 (0.66)
3e 4 - f8 (0.59)
3c 4 - f0 (0.57)
d0 1 - d0 (0.49)
bc 1 - bc (0.44)
b8 1 - b8 (0.44)
这是查找内存泄漏原因的正确方法,因为我没有看到繁忙字节的百分比变化,这可以解释这个特定堆的 ~0.5GB 内存增加?另外,我如何确定此堆中包含哪些对象/模块以及谁在进行分配?
由于您可以拍摄快照,因此我建议使用 UMDH,它是与 WinDbg 一起设置的调试器工具集的一部分。那里有很多教程/示例,但基本流程如下所示:
-
启用堆栈跟踪日志记录:
gflags.exe -i processName.exe +ust
-
运行进程,并记下进程 ID(在任务管理器中)
-
拍摄第一个内存快照(将 1234 替换为进程 ID)
umdh.exe -p:1234 -f:mem1.txt
-
让进程运行一段时间(并泄漏内存)
-
生成第二个快照
umdh.exe -p:1234 -f:mem2.txt
-
创建包含大小和堆栈跟踪的报告
umdh.exe mem1.txt mem2.txt >mem_compare.txt
-
禁用流程的堆栈跟踪
gflags.exe -i processName.exe -ust
报告将如下所示:
+ a556ae5 ( a556ae5 - 0) 7 allocs BackTraceC0E
+ 7 ( 7 - 0) BackTraceC0E allocations
ntdll!RtlLogStackBackTrace+00000007
ntdll!RtlAllocateHeap+0000023A
ntdll!RtlDebugAllocateHeap+000000B5
ntdll!RtlpAllocateHeap+000000C4
ntdll!RtlAllocateHeap+0000023A
ntdll!LdrpTagAllocateHeap+00000025
ntdll!LdrpTagAllocateHeap29+00000015
processName!malloc+00000016
processName!createLotsOfObjects+00000712
...
这表明,在两个快照之间,使用此堆栈跟踪分配了 ~170megs(0xa55ae5 字节)的内存。