为什么是大型对象堆? 我们为什么要关心这个?

我已经阅读了关于生成和大型对象堆的文章,但是我仍然不能理解拥有大型对象堆的意义(或好处)是什么?

如果 CLR 仅仅依赖于第2代来存储大型对象(考虑到 Gen0和 Gen1的阈值太小而不能这样做) ,那么会出现什么问题(在性能或内存方面) ?

47434 次浏览

我不是 CLR 方面的专家,但我可以想象,为大型对象专门设置一个堆可以防止对现有的代堆进行不必要的 GC 扫描。分配大型对象需要大量的 相邻的空闲内存。为了从代堆中分散的“空洞”中提供这些信息,需要频繁的压缩(这只能在 GC 循环中完成)。

主要原因是,一个进程不太可能(而且很可能是糟糕的设计)创建大量短期存活的大对象,因此 CLR 将大对象分配给一个单独的堆,在这个单独的堆上,CLR 按照与常规堆不同的计划运行 GC。http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

小对象堆(SOH)和大对象堆(LOH)的本质区别在于,SOH 中的内存在收集时被压缩,而 LOH 不是,如 这篇文章所示。压缩大型物体的成本很高。与本文中的示例类似,比如在内存中移动一个字节需要2个周期,然后在2GHz 计算机中压缩一个8MB 对象需要8ms,这是一个很大的开销。考虑到大型对象(大多数情况下是数组)在实践中相当常见,我想这就是为什么微软在内存中钉大型对象并提出 LOH 的原因。

顺便说一下,根据 这篇文章,LOH 通常不会产生内存片段问题。

如果对象的大小大于某个固定值(。NET 1) ,然后 CLR 将其放入大对象堆中。这种做法最大限度地优化了:

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

垃圾收集不仅可以删除未引用的对象,还可以对堆进行 契约。这是一个非常重要的优化。它不仅使内存使用效率更高(没有未使用的漏洞) ,而且使 CPU 缓存效率更高。高速缓存在现代处理器中是一个非常重要的数量级,它比内存总线更快。

Compacting is done simply by copying bytes. That however takes time. The larger the object, the more likely that the cost of copying it outweighs the possible CPU cache usage improvements.

所以他们运行了一系列基准来确定收支平衡点。并到达85,000字节的截止点,在这个截止点上,复制不再改进 perf。双精度数组有一个特殊的例外,当数组中有超过1000个元素时,它们被认为是“大的”。这是对32位代码的另一种优化,大型对象堆分配器有一个特殊的属性,它将内存分配到对齐为8的地址,不像普通的分代分配器只将内存分配到对齐为4的地址。这种对齐对于双精度浮点数来说是一个大问题,读取或写入一个错误对齐的双精度数是非常昂贵的。奇怪的是,微软稀少的信息从来没有提到长数组,不知道这是怎么回事。

Fwiw, there's lots of programmer angst about the large object heap not getting compacted. This invariably gets triggered when they write programs that consume more than half of the entire available address space. Followed by using a tool like a memory profiler to find out why the program bombed even though there was still lots of unused virtual memory available. Such a tool shows the holes in the LOH, unused chunks of memory where previously a large object lived but got garbage collected. Such is the inevitable price of the LOH, the hole can only be re-used by an allocation for an object that's equal or smaller in size. The real problem is assuming that a program should be allowed to consume 所有 virtual memory at any time.

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

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


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