Memory leaks 碰撞转储中的2代自由块过多

Memory leaks 碰撞转储中的2代自由块过多,memory-leaks,out-of-memory,clr,windbg,memory-fragmentation,Memory Leaks,Out Of Memory,Clr,Windbg,Memory Fragmentation,检查崩溃转储文件是否存在客户端报告的内存不足异常时,的结果!DumpHeap-stat显示,45000个“Free”类型的对象占用了575MB的内存,我认为由于大小的原因,大多数对象必须驻留在Gen2中 我首先查找问题的地方是大型对象堆(LOH)和固定对象。包含可用空间的大型对象堆只有70MB,因此这不是问题所在,正在运行!gchandles显示: GC句柄统计信息: 强句柄:155 销柄:265 异步固定句柄:8 参考计数句柄:163 弱长句柄:0 弱短句柄:0 其他句柄:0 与自由对象的数

检查崩溃转储文件是否存在客户端报告的内存不足异常时,
的结果!DumpHeap-stat
显示,45000个“Free”类型的对象占用了575MB的内存,我认为由于大小的原因,大多数对象必须驻留在Gen2中

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

GC句柄统计信息:
强句柄:155
销柄:265
异步固定句柄:8
参考计数句柄:163
弱长句柄:0
弱短句柄:0
其他句柄:0
与自由对象的数量(45000)相比,控制柄的数量非常少(大约600个)。对我来说,这排除了由钉住引起的自由块

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

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

自由对象在哪一代? 我想,由于尺寸的原因,必须住在第二代

大小与世代无关。要找出空闲块所在的生成,可以执行以下步骤:

来自
!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代地址),仅转储特定代的空闲对象:

在我的简单例子中,第2代中有两个自由对象

销柄
600个固定对象不应导致45.000个可用内存块。根据我的经验,600个固定手柄还是很多。但首先,检查空闲内存块驻留在哪一代。

执行一次!address–summary要获得进程的概述,请注意堆的使用情况(即本机堆),以防发生本机泄漏。如果您需要更多帮助,请用结果更新您的帖子。@KjellGunnar-的输出!地址摘要显示只有146MB用于本机堆。大部分是在该部分中,该部分应该主要是与CLR相关的项,并且大部分来自575MB的自由对象。265实际上是相当多的固定句柄。碎片是累积的,因此在许多有265个固定句柄的地面军事系统上,碎片会累积起来。@SteveJohnson-你确定固定句柄的碎片是累积的吗。我在阅读,它说第0代、第1代和第2代应该尽可能地压缩,所以这不会消除钉住手柄的累积碎片影响的可能性吗?我提到它们都是第2代,因为第0代和第1代的大小与自由对象的100 MB相比几乎微不足道。我运行了您请求的命令,基本上100%的自由对象都是Gen2。根据固定的句柄,它们中的大多数是System.Drawing.Internal.GPStream,看起来像是流泄漏,但即使考虑到我不知道其中的几百个怎么会产生如此巨大的可用空间。@Chiunesughihara:这意味着在下一代垃圾收集中,堆将被再次压缩,并且可能会重用大量内存,对吗?不幸的是,在此之前,您遇到了OOM异常。您的应用程序中是否有任何地方可以释放大量内存,并且可以在代码中调用GC.Collect()?您的应用程序使用哪个.NET版本?不幸的是,应用程序太大,我无法缩小可以调用GC.Collect的特定区域。我知道理论上你不应该打电话给它,但是听说过关于GC不能完全正常工作的谣言,以及手动呼叫GC.Collect以防止应用程序崩溃(不确定它是否正确)。我们正在使用的.NET版本是4.0。@Chiunesughihara:在.NET2.0中,我遇到过这样一种情况,.NET更喜欢CPU密集型任务而不是垃圾收集,尽管内存已经很低了。这导致了
OutOfMemoryException
。在CPU密集型任务开始之前,在一个众所周知的地方调用
GC.Collect()
,解决了这个问题。不过,这应该是最后的手段。请记住,调用
GC.Collect()
会将所有对象的生成量增加1,因此经常调用它会使许多对象成为Gen2的一部分,而Gen2很少被收集。
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