You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windbg下.NET进程堆内存与对象总内存不匹配的排查疑问

.NET内存泄漏排查:!address -summary与!dumpheap -stat内存差异分析

问题背景

调试.NET进程排查内存泄漏时,执行!address -summary得到如下结果:

0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                    648     7dbb`1f7df000 ( 125.731 TB)           98.23%
<unknown>                              7417      241`d1e9c000 (   2.257 TB)  99.47%    1.76%
Heap                                   1304        2`e58f8000 (  11.587 GB)   0.50%    0.01%
Image                                  2938        0`22f13000 ( 559.074 MB)   0.02%    0.00%
Stack                                   195        0`05f00000 (  95.000 MB)   0.00%    0.00%
Other                                    30        0`001e7000 (   1.902 MB)   0.00%    0.00%
TEB                                      65        0`00082000 ( 520.000 kB)   0.00%    0.00%
PEB                                       1        0`00001000 (   4.000 kB)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_MAPPED                             5588      200`16897000 (   2.000 TB)  88.16%    1.56%
MEM_PRIVATE                            3424       44`a7067000 ( 274.610 GB)  11.82%    0.21%
MEM_IMAGE                              2938        0`22f13000 ( 559.074 MB)   0.02%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                648     7dbb`1f7df000 ( 125.731 TB)           98.23%
MEM_RESERVE                            2348      241`9ef5b000 (   2.256 TB)  99.44%    1.76%
MEM_COMMIT                             9602        3`418b6000 (  13.024 GB)   0.56%    0.01%

其中Heap总大小为11.587 GB,但执行!dumpheap -stat后,所有托管对象总内存仅约286 MB,远小于前者,需明确剩余内存去向及排查方法。

差异原因

!address -summary中的Heap是Windows系统层面的堆范畴,包含:

  • .NET托管堆的保留/提交内存
  • 进程中所有非托管堆(如C++堆、Win32 API分配的堆)
  • CLR内部使用的非托管内存
  • 堆内存碎片

而!dumpheap -stat仅统计托管堆中已分配的托管对象内存,两者统计范围完全不同,因此出现巨大差异。

剩余内存的可能去向及排查方法

1. 托管堆的保留但未提交内存

CLR会预先保留大块内存区域(MEM_RESERVE状态),仅在实际需要时提交(MEM_COMMIT),!dumpheap不会统计未提交的保留内存。

  • 排查命令:
    !eeheap -gc
    
    该命令会输出每个托管堆段的Reserved和Committed大小,对比两者差值即可得到未提交的保留内存总量。

2. 非托管内存占用

非托管堆的内存分配完全脱离.NET托管系统,不会被!dumpheap统计,但属于!address的Heap范畴。

  • 排查步骤:
    1. 查看所有非托管堆的摘要,定位占用较大的堆:
      !heap -s
      
    2. 对目标堆执行统计分析,查看内存分配分布:
      !heap -stat -h <堆Handle>
      
      (替换<堆Handle>为步骤1中找到的大堆句柄,如0x0000000000180000)
    3. 过滤大尺寸分配记录,进一步查看调用栈:
      !heap -flt s <大小阈值>  # 例如过滤大于1MB的分配:!heap -flt s 1048576
      !heap -p -a <分配地址>    # 查看指定分配地址的调用栈详情
      

3. CLR内部的非托管内存

CLR自身运行会占用部分非托管内存(如JIT编译缓存、GC内部结构、线程本地存储等),这些内存属于Heap范畴但不被!dumpheap统计。

  • 排查命令:
    • 查看CLR加载器的内存使用:
      !eeheap -loader
      
    • 查看线程相关的内存占用:
      !eeheap -threads
      

4. 堆内存碎片

托管堆或非托管堆中的内存碎片(已分配块之间的空闲空间)属于Heap的已提交部分,但未被对象占用,因此不会被!dumpheap统计。

  • 排查方法:
    • 托管堆碎片:执行!dumpheap -type Free查看托管堆中的空闲块分布
    • 非托管堆碎片:通过!heap -stat输出的Fragmentation字段判断碎片程度

整体排查流程

  1. 用!eeheap -gc确认托管堆保留/提交差异,排除未提交内存的影响
  2. 用!heap -s定位占用大的非托管堆,深入分析非托管分配
  3. 用!eeheap -loader和!eeheap -threads检查CLR内部非托管内存
  4. 检查托管/非托管堆的内存碎片情况

内容的提问来源于stack exchange,提问作者Phiber

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:29:50