托管堆碎片

时间:2011-06-24 08:41:15

标签: debugging memory-leaks clr heap-fragmentation

我试图了解堆碎片的工作原理。以下输出告诉我什么?

这个堆是否过于分散?

我有243010个“自由对象”,总共53304764个字节。堆中的那些“自由对象”空间是否曾经包含对象但是现在是否已经收集了这些空间?

如何强制碎片堆清理?

!dumpheap -type Free -stat
total 243233 objects
Statistics:
      MT    Count    TotalSize Class Name
0017d8b0   243010     53304764      Free

2 个答案:

答案 0 :(得分:7)

这取决于堆的组织方式。你应该看看Gen 0,1,2中分配了多少内存,以及你有多少可用内存与总使用内存相比。 如果您使用了500 MB托管堆但是50 MB是免费的,那么您的表现相当不错。如果您执行内存密集型操作(如创建许多WPF控件并释放它们),您需要在短时间内获得更多内存,但.NET在分配后不会将内存返回给操作系统。 GC尝试识别分配模式,并且往往会使内存占用率保持较高,尽管当前的堆大小太大,直到您的机器在物理内存上运行不足为止。

我发现使用psscor2更容易.NET 3.5,它有一些很酷的命令,如ListNearObj,你可以找到你的内存孔周围的对象(固定对象?)。使用psscor2的命令,你有更好的机会找出堆中真正发生的事情。大多数命令也可以在.NET 4中的SOS.dll中使用。

回答你原来的问题:是的,自由对象是托管堆上的空白,它可能只是你在GC分区上最后一个分配对象之后的空闲内存块。或者如果你这样做!DumpHeap带有GC段的起始地址,你会看到在该托管堆段中分配的对象以及作为GC收集对象的自由对象。

这个内存空洞通常在Gen2中发生。自由对象之前和之后的对象地址会告诉您洞内可能存在固定对象的内容。由此您可以确定您的分配历史记录并在需要时对其进行优化。 您可以使用

找到GC堆的地址
0:021> !EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x101da9cc
generation 1 starts at 0x10061000
generation 2 starts at 0x02aa1000
ephemeral segment allocation context: none
 segment     begin allocated  size
02aa0000  02aa1000**  03836a30  0xd95a30(14244400)
10060000  10061000**  103b8ff4  0x357ff4(3506164)
Large object heap starts at 0x03aa1000
 segment     begin allocated  size
03aa0000  03aa1000  03b096f8  0x686f8(427768)
Total Size:              Size: 0x115611c (18178332) bytes.
------------------------------
GC Heap Size:            Size: 0x115611c (18178332) bytes.

在那里你看到你有02aa1000和10061000的堆。 使用!DumpHeap 02aa1000 03836a30,您可以转储GC堆段。

!DumpHeap 02aa1000  03836a30  
    Address  MT             Size
    ...
    037b7b88 5b408350       56     
    037b7bc0 60876d60       32     
    037b7be0 5b40838c       20     
    037b7bf4 5b408350       56     
    037b7c2c 5b408728       20     
    037b7c40 5fe4506c       16     
    037b7c50 60876d60       32     
    037b7c70 5b408728       20     
    037b7c84 5fe4506c       16     
    037b7c94 00135de8   519112 Free
    0383685c 5b408728       20     
    03836870 5fe4506c       16     
    03836880 608c55b4       96   
    ....

在那里你找到了你的空闲内存块,这是一个已经GCed的对象。您可以转储周围的对象(输出按地址排序)以确定它们是否被固定或具有其他不寻常的属性。

答案 1 :(得分:0)

你有50MB的RAM作为可用空间。这不好。

让.NET从进程中分配16MB的块,我们确实存在碎片问题。 在.NET中出现碎片的原因有很多。

查看herehere。 在你的情况下,它可能是一个钉扎。因为53304764/243010每个对象产生219.35个字节 - 比LOH对象低得多。