托管堆碎片化

10

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

这个堆是否过度碎片化了?

我有243010个“free objects”,总共53304764字节。这些“free object”空间是在堆中曾经包含对象但现在已被垃圾回收的空间吗?

如何强制清理碎片化的堆?

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

7
这取决于您的堆是如何组织的。您应该查看分配了多少Gen 0,1,2中的内存以及您在那里拥有多少自由内存与总使用内存相比。如果您使用了500MB托管堆,但有50MB是空闲的,那么您做得很好。如果您执行内存密集型操作,例如创建许多WPF控件并释放它们,则需要更多内存短时间内,但.NET不会将内存返还给操作系统一旦您分配了它。GC尝试识别分配模式,并倾向于保持您的内存占用高,尽管您当前的堆大小过大,直到您的计算机的物理内存不足为止。
我发现使用.NET 3.5的psscor2要容易得多,它具有一些很酷的命令,例如ListNearObj,您可以找出哪些对象在您的内存洞周围(固定对象?)。使用psscor2的命令,您有更好的机会找出堆中实际发生的情况。大多数命令也在.NET 4中的SOS.dll中可用。
回答您最初的问题:是的,自由对象是托管堆上的间隙,可以简单地是您在GC段上分配的最后一个对象后的空闲内存块。或者,如果您使用GC段的起始地址进行!DumpHeap,则会看到在该托管堆段中分配的对象以及您的自由对象,这些对象是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   
    ....

在这里,您可以找到已经进行过垃圾回收的自由内存块对象。您可以转储周围的对象(输出按地址排序),以查看它们是否被固定或具有其他异常属性。


堆碎片化是否可能发生(并最终导致OutOfMemoryException),没有固定的对象?我了解GC是清理碎片化堆的工具。没有固定的对象,GC仍然可能无法及时清理堆以满足新的内存请求吗?除了固定的对象之外,还有什么原因会导致碎片化的堆无法在需要进行新分配时及时清理? - bitbonk
如果分配速率非常高,您可能会耗尽内存。CLR确实内置了Sleep(50)以限制您的分配请求,如果您正在紧密循环中进行分配,则为GC提供清理的机会,但这可能不足够。 - Alois Kraus

0

您有50MB的RAM可用空间。这不太好。

由于.NET从进程中分配16MB的块,我们确实存在碎片问题。 在.NET中发生碎片化有很多原因。

请看这里这里。 在您的情况下,可能是固定引用导致的。因为53304764 / 243010每个对象占用219.35字节 - 远低于LOH对象。


好的,我有50MB的可用空间,但是这个“Count”告诉我什么呢? 我知道我经常创建大量小对象(例如每10秒创建一个约20000个WPF控件的网格,但旧的控件变得无法引用,没有内存泄漏)。这可能是原因吗?还是固定对象更有可能呢? - bitbonk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接