为什么大对象堆,为什么我们在意?

我已阅读关于世代和大对象堆。 但我仍然不明白什么是大对象堆的意义(或好处)?

如果CLR只是依赖第二代(考虑到Gen0和Gen1的阈值对处理Large对象来说很小),那么会出现什么问题(在性能或内存方面)呢?

垃圾收集不仅仅是摆脱了未被引用的对象,它也压缩了堆。 这是一个非常重要的优化。 它不只是使内存使用效率更高(没有未使用的漏洞),它使CPUcaching更有效率。 高速caching在现代处理器上是非常重要的,它们比内存总线快很多。

压缩只需通过复制字节来完成。 但是这需要时间。 对象越大,复制成本越可能超过可能的CPUcaching使用率改进。

所以他们运行了一堆基准来确定盈亏平衡点。 到达85,000字节作为复制不再改善性能的截止点。 对于double数组有一个特殊的例外,当数组超过1000个元素时,它们被认为是“大”的。 这是32位代码的另一个优化,大对象堆分配器有特殊的属性,它分配内存的地址alignment到8,不像常规分配器,只分配alignment到4.这alignment是一个重大的双,阅读或写错误的alignment是非常昂贵的。 奇怪的微软信息从来没有提到长arrays,不知道这是怎么回事。

Fwiw,有很多程序员对这个大的对象堆没有被压缩感到焦虑。 当他们编写占用整个可用地址空间的一半以上的程序时,这总是会被触发。 随后使用一个像内存分析器这样的工具来找出程序被轰炸的原因,即使仍有大量未使用的虚拟内存可用。 这样的工具显示了L holes中的空洞,未曾使用过的大块物体曾经是一个大物体,但却被垃圾收集起来。 这是陆委会不可避免的代价,这个洞只能通过分配一个相等或较小的物件来重复使用。 真正的问题是假设一个程序应该被允许随时消耗所有的虚拟内存。

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

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


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

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

顺便说一下 ,根据这个post ,LOH通常不会产生内存碎片问题。

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

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

主要原因是一个进程不大可能(也可能是不好的devise),一个进程会创build大量短暂的大对象,所以CLR将大对象分配给一个单独的堆,在这个堆上运行GC的时间与常规堆不同。 http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

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