为什么大对象堆和我们为什么关心?

时间:2012-01-21 09:06:29

标签: .net garbage-collection clr large-object-heap

我已阅读有关Generations和Large object heap的内容。但我仍然不明白拥有大型对象堆的意义(或好处)是什么?

如果CLR只是依赖于第2代(考虑到Gen0和Gen1的阈值很小来处理大型对象)来存储大型对象,那么可能出现了什么问题(在性能或内存方面)?

5 个答案:

答案 0 :(得分:186)

垃圾收集不仅可以清除未引用的对象,还可以压缩堆。这是一个非常重要的优化。它不仅可以提高内存使用效率(没有未使用的漏洞),还可以提高CPU缓存效率。缓存在现代处理器上非常重要,它们比内存总线快一个数量级。

只需复制字节即可完成压缩。然而,这需要时间。对象越大,复制成本的可能性就越大,可能会超过可能的CPU缓存使用率。

所以他们运行了一系列基准来确定盈亏平衡点。并且到达85,000字节作为截止点,其中复制不再提高性能。对于double数组的特殊例外,当数组有超过1000个元素时,它们被认为是“大”。这是32位代码的另一个优化,大对象堆分配器具有特殊属性,它在与8对齐的地址处分配内存,这与仅分配对齐的常规分组分配器不同。这种对齐对双重来说是一个大问题,阅读或写一个错位的双倍是非常昂贵的。奇怪的是,稀疏的微软信息从未提及多长的数组,不确定是什么。

Fwiw,有很多程序员担心大型对象堆没有被压缩。当它们编写占用整个可用地址空间一半以上的程序时,它总会被触发。接下来使用像内存分析器这样的工具来找出程序轰炸的原因,即使仍有大量未使用的虚拟内存可用。这样的工具显示了LOH中的漏洞,未使用的内存块,以前存在大型对象,但收集了垃圾。这是LOH不可避免的代价,只能通过分配大小相等或更小的对象来重复使用这个洞。真正的问题是假设应该允许程序随时使用所有虚拟内存。

只需在64位操作系统上运行代码就可以完全消失的问题。 64位进程具有 8 TB 的虚拟内存地址空间,比32位进程多3个数量级。你不能用完漏洞。

长话短说,LOH使代码运行更高效。以使用可用虚拟内存地址空间为代价的效率较低。


UPDATE,.NET 4.5.1现在支持压缩LOH,GCSettings.LargeObjectHeapCompactionMode属性。请注意后果。

答案 1 :(得分:8)

如果对象的大小大于某个固定值(.NET 1中为85000字节),则CLR将其置于大对象堆中。这优化了:

  1. 对象分配(小对象不与大对象混合)
  2. 垃圾收集(仅在完整GC上收集LOH)
  3. 内存碎片整理(LOH 从不很少压缩)

答案 2 :(得分:7)

小对象堆(SOH)和大对象堆(LOH)的本质区别在于,SOH中的内存在收集时会被压缩,而LOH则不会如this article所示。压缩大型物体需要花费很多。与本文中的示例类似,假设在内存中移动一个字节需要2个周期,然后在2GHz计算机中压缩8MB对象需要8ms,这是一个很大的成本。考虑到大对象(在大多数情况下是阵列)在实践中非常普遍,我想这就是为什么微软将大对象固定在内存中并提出LOH的原因。

BTW,根据this post,LOH通常不会产生内存碎片问题。

答案 3 :(得分:1)

主体是一个进程不太可能(并且很可能是糟糕的设计)创建大量短期大型对象,因此CLR将大对象分配给一个单独的堆,在该堆上它以不同的时间表运行GC到常规堆。 http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

答案 4 :(得分:0)

我不是CLR的专家,但我认为拥有大型对象的专用堆可以防止对现有世代堆进行不必要的GC扫描。分配大对象需要大量的连续空闲内存。为了从代代堆中分散的“漏洞”中提供它,你需要频繁的压缩(只在GC循环中完成)。