为啥 windbg> !EEHeap -gc 显示的托管堆比 VMMAP.exe 小得多?

Posted

技术标签:

【中文标题】为啥 windbg> !EEHeap -gc 显示的托管堆比 VMMAP.exe 小得多?【英文标题】:Why does windbg> !EEHeap -gc show a much smaller managed heap than VMMAP.exe?为什么 windbg> !EEHeap -gc 显示的托管堆比 VMMAP.exe 小得多? 【发布时间】:2014-06-16 22:58:48 【问题描述】:

我有一个 C# 应用程序,其内存使用量随时间增加。我已经定期进行用户模式转储,并在加载 sos 后,运行 !EEHeap -gc 来监控托管堆大小。在 windbg/sos 中,我看到它从 14MB 开始增长到 160MB,然后缩小到 15MB,但应用程序“Private Bytes”从未显着减少。我已经确定了导致“Private Bytes”增加的活动,因此我可以控制何时发生内存增长。

我尝试运行 Vmmap.exe 并注意到它报告了大约 360MB 的托管堆,快速转储并使用 windbg/sos/eeheap -gc 我只看到 15MB。

为什么我会看到如此不同的价值观? 托管堆真的是 vmmap.exe 报告的内容吗?

如何在 windbg 中检查托管堆的这个区域?

【问题讨论】:

【参考方案1】:

您不能使用 WinDbg 闯入 .NET 应用程序,然后同时运行 VMMap。这将导致 VMMap 挂起。你也可以不做相反的事情:先启动VMMap,然后进入WinDbg,然后刷新VMMap中的值。

因此,VMMap 显示的值可能永远不会相等,因为这些数字来自不同的时间点。不同的时间点也可能意味着垃圾收集器已经运行。如果应用程序没有发生太大变化,则值应该接近。

在我的测试中,VMMap 中托管堆的已提交部分是!eeheap -gc!eeheap -loader 的总和,这听起来很合理。

鉴于!eeheap -gc 的输出,我们在第 2 代 (11aa0000) 处开始 GC 堆,大小仅为 3.6 MB。

Number of GC Heaps: 1
generation 0 starts at 0x0000000011d110f8
generation 1 starts at 0x0000000011cd1130
generation 2 starts at 0x0000000011aa1000
...
GC Heap Size          0x374a00(3623424)

!address 提供详细信息:

...
+        0`11aa0000        0`11ef2000        0`00452000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     <unknown>  
         0`11ef2000        0`21aa0000        0`0fbae000 MEM_PRIVATE MEM_RESERVE                                    <unknown>  
         0`21aa0000        0`21ac2000        0`00022000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     <unknown>  
         0`21ac2000        0`29aa0000        0`07fde000 MEM_PRIVATE MEM_RESERVE                                    <unknown>
+        0`29aa0000        0`6ca20000        0`42f80000             MEM_FREE    PAGE_NOACCESS                      Free 
...

虽然没有记录,但我相信新段从 11aa0000 开始,由 + 符号表示。 GC 段在 29aa0000 结束,这也是下一段的起点。交叉检查:.NET 内存应在最后一列报告为 &lt;unknown&gt; - 好的。

总 GC 大小(保留 + 已提交)为

?29aa0000-11aa0000
Evaluate expression: 402653184 = 00000000`18000000

这是 402 MB 或 393.216 kB,在我的情况下,它非常接近 VMMap 报告的 395.648 kB。

如果你有更多的 GC 堆,整个过程需要更多的努力。因此,我通常采用捷径,如果您知道除了调用 VirtualAlloc() 的 .NET 之外没有其他任何东西,那就可以了。键入!address -summary,然后查看第一个&lt;unknown&gt; 条目:

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                    144      7ff`d8a09000 (   7.999 Tb)           99.99%
<unknown>                               180        0`1a718000 ( 423.094 Mb)  67.17%    0.01%
...

【讨论】:

谢谢!我正在添加由于您的输入而发现的内容,作为澄清的另一个答案。添加评论太长了。 "这是 402 MB 或 393.216 kB,在我的情况下非常接近 VMMap 报告的 395.648 kB。"这背后的数学原理可以澄清吗? @rb_:对于程序员:402.653.184 字节 = 393.216 kB = 384 MB。对于硬盘供应商:402.653.184 字节 = 402 MB 哈哈哈!我的错误是我误读了原始声明。我将把它留在这里以提醒您咖啡很重要。【参考方案2】:

非常感谢您的详细回答。非常感谢。

我很清楚 windbg 与 VMmap 对程序的访问/控制。由于我可以通过外部操作导致泄漏,因此我很确定由于我停止了活动,因此样本之间的内存不会增长太多。

我一直依赖 !eeheap -gc 的最后一行输出:

GC 堆大小:大小:0xed7458 (15561816) 字节。

我认为这个数字必须是正在使用的托管堆的数量(其中包含未释放的对象)。我将“!eeheap -gc”报告的每个 SOH 和 LOH 的所有“大小”字节相加,它与上述值匹配。

我运行了 VMmap,拍了一张快照并退出了 VMmap。然后我用windbg附加了这个过程。您使用 !address 的技术最具启发性。我使用的是 12 处理器服务器系统,因此每个处理器都有 SOH 和 LOH,即总和为 12。带头,“!eeheap -gc”的输出包含所有堆的段。我将它们全部输入“!address”并将它们的大小相加(加上 !eeheap -loader 报告的大小)。结果是 335,108K,这在我期望在经过的时间内看到的变化范围内(在 600K 以内)。 VMmap 托管堆似乎是托管堆使用的所有内存段的总量(我没有检查保留的数字)。所以现在我明白了为什么“!eeheap -gc”报告的总数比VMmap 显示的要少得多。 谢谢!

【讨论】:

如果我的回答对您有帮助,您可以按向上箭头进行投票。如果它完全回答了您的问题,您可以按复选标记将其设为接受的答案。

以上是关于为啥 windbg> !EEHeap -gc 显示的托管堆比 VMMAP.exe 小得多?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在 WinDbg 中看到的是 ntdll 反汇编,而不是我的汇编代码?

通过查看Windbg中的变量值去定位C++软件异常问题

vc++ windbg dump 调试 疑问!

Windbg入门

WinDbg单机调试

再谈Windbg脚本装载指令