托管应用程序中的非托管内存泄漏

本文关键字:内存 泄漏 应用程序 | 更新日期: 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 一起设置的调试器工具集的一部分。那里有很多教程/示例,但基本流程如下所示:

  1. 启用堆栈跟踪日志记录:

    gflags.exe -i processName.exe +ust

  2. 运行进程,并记下进程 ID(在任务管理器中)

  3. 拍摄第一个内存快照(将 1234 替换为进程 ID)

    umdh.exe -p:1234 -f:mem1.txt

  4. 让进程运行一段时间(并泄漏内存)

  5. 生成第二个快照

    umdh.exe -p:1234 -f:mem2.txt

  6. 创建包含大小和堆栈跟踪的报告

    umdh.exe mem1.txt mem2.txt >mem_compare.txt

  7. 禁用流程的堆栈跟踪

    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 字节)的内存。