【问题标题】:Excessive Gen 2 Free Blocks in Crash Dump故障转储中过多的第 2 代空闲块
【发布时间】:2015-06-08 15:54:08
【问题描述】:

在检查崩溃转储文件以查找客户端报告的内存不足异常时,!DumpHeap -stat 的结果显示,45,000 个“免费”类型的对象占用了 575MB 的内存,我认为其中大部分都必须由于大小,位于第 2 代。

我首先寻找问题的地方是大对象堆 (LOH) 和固定对象。包含可用空间的大型对象堆只有 70MB,所以这不是问题,运行 !gchandles 显示:

GC Handle Statistics:
Strong Handles:        155
Pinned Handles:        265
Async Pinned Handles:  8
Ref Count Handles:     163
Weak Long Handles:     0
Weak Short Handles:    0
Other Handles:         0

与空闲对象的数量(45,000)相比,这是非常少的句柄(大约 600)。对我来说,这排除了由固定引起的空闲块。

我还检查了空闲块本身,看看它们是否具有一致的大小,但经过检查,大小差异很大,从不到 5MB 到只有大约 12 个字节左右。

任何帮助将不胜感激!我不知所措,因为存在碎片,但没有迹象表明它是由我知道要查看的两个地方引起的,即大对象堆 (LOH) 和固定句柄。

【问题讨论】:

  • 做一个 !address –summary 以获得您的进程的概述,注意堆使用情况(这是本机堆),以防您有本机泄漏。如果您需要更多帮助,请使用结果更新您的帖子。
  • @KjellGunnar - !address -summary 的输出显示只有 146MB 用于本机堆。大部分在 部分,应该主要是与 CLR 相关的项目,其中大部分来自 575MB 的免费对象。
  • 265 实际上是相当多的固定句柄。碎片是累积的,所以在许多有大约 265 个固定句柄的 GC 上,它会加起来。
  • @SteveJohnson - 你确定这是关于固定句柄碎片累积的真实情况。我正在阅读OutOfMemoryException and Pinning,它说第 0、1 和 2 代应该在可能的情况下进行压缩,所以这不会消除固定句柄造成累积碎片影响的可能性吗?

标签: memory-leaks out-of-memory clr windbg memory-fragmentation


【解决方案1】:

空闲对象在哪一代?

由于大小,我认为必须居住在第 2 代

大小与世代无关。要找出空闲块位于哪一代,您可以按照以下步骤操作:

!dumpheap -stat -type Free获取方法表:

0:003> !dumpheap -stat -type Free
total 7 objects
Statistics:
      MT    Count    TotalSize Class Name
00723538        7          100      Free
Total 7 objects

!eeheap -gc,获取世代的起始地址。

0:003> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x026a1018
generation 1 starts at 0x026a100c
generation 2 starts at 0x026a1000
ephemeral segment allocation context: none
 segment    begin allocated     size
026a0000 026a1000  02731ff4 0x00090ff4(593908)
Large object heap starts at 0x036a1000
 segment    begin allocated     size
036a0000 036a1000  036a3250 0x00002250(8784)
Total Size   0x93244(602692)
------------------------------
GC Heap Size   0x93244(602692)

然后通过传递开始和结束地址(例如这里的第 2 代)仅转储特定代的空闲对象:

0:003> !dumpheap -mt 00723538 0x026a1000 0x026a100c
 Address       MT     Size
026a1000 00723538       12 Free
026a100c 00723538       12 Free
total 2 objects
Statistics:
      MT    Count    TotalSize Class Name
00723538        2           24      Free
Total 2 objects

所以在我的简单例子中,第 2 代中有 2 个空闲对象。

固定手柄

600 个固定对象不应导致 45.000 个空闲内存块。仍然根据我的经验,600 个固定手柄很多。但首先,检查空闲内存块位于哪一代。

【讨论】:

  • 我提到它们都是第 2 代,因为与 100 MB 的免费对象相比,第 0 代和第 1 代的大小几乎微不足道。我运行了您请求的命令,基本上 100% 的免费对象是第 2 代。根据固定句柄,它们中的大多数是 System.Drawing.Internal.GPStream 这似乎是流泄漏,但即使我不知道其中几百个如何能创造出如此大量的可用空间。
  • @ChiuneSugihara:这意味着在下一次 Gen2 垃圾回收中,堆将再次被压缩,并且可能会重用大量内存,对吧?不幸的是,在那之前你遇到了 OOM 异常。您的应用程序中是否有任何地方知道释放了大量内存并且您可以在代码中调用 GC.Collect() ?您的应用程序使用哪个 .NET 版本?
  • 不幸的是,应用程序太大,我无法缩小可以调用 GC.Collect 的特定区域。我知道理论上您不需要调用它,但听说过有关 GC 无法完全正常工作以及手动调用 GC.Collect 以防止应用程序崩溃的谣言(不确定它是否属实)。我们使用的 .NET 版本是 4.0。
  • @ChiuneSugihara:在 .NET 2.0 中,我遇到过这样的情况,即 .NET 更喜欢 CPU 密集型任务而不是垃圾收集,尽管内存已经非常低。这导致了OutOfMemoryException。在 CPU 密集型任务开始之前,在众所周知的地方调用 GC.Collect() 可以解决问题。不过,它应该仍然是最后的手段。请记住,调用GC.Collect() 会将所有对象的生成增加1,因此经常调用它会使很多对象成为Gen2 的一部分,很少被收集。
猜你喜欢
  • 1970-01-01
  • 2016-09-09
  • 2016-03-21
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多