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

Man*_*ani 96 .net clr garbage-collection large-object-heap

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

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

Han*_*ant 189

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

通过复制字节简化压缩.然而,这需要时间.对象越大,复制它的成本就越有可能超过可能的CPU缓存使用量改进.

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

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

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

简而言之,LOH使代码运行更高效.以使用可用虚拟内存地址空间为代价的效率较低.


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

  • 当然,x64上的碎片问题也是一样的.在启动之前,运行服务器进程只需要几天时间. (6认同)
  • @Hans Passant,您能否澄清一下x64系统,你的意思是这个问题完全消失了吗? (2认同)
  • @HansPassant 请你详细说明一下这个说法:“垃圾收集一个 4 TB 的堆需要多长时间是你无法避免在它接近那个之前很久就会发现的。” (2认同)

ole*_*sii 8

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

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


gra*_*eot 7

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

BTW,根据这篇文章,LOH通常不会产生内存碎片问题.


Myl*_*ell 5

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