Large Object Heap内存碎片在.NET 4.5中的改进

2021-04-16
.NET 4.5已然到来,预览了解了下Large Object Heap在.NET 4.5中的效能改进。借此和大家来探讨下。本文不讨论Loder Heap,SOH(samll object heap),LOH(large object heap),JIT Heap,Process Heap关系和区别。也不会着重讨论GC,单论Large Object Heap在.NET 4.5中的改进。

  .NET 4.5已然到来,预览了解了下Large Object Heap在.NET 4.5中的效能改进。借此和大家来探讨下。本文不讨论Loder Heap,SOH(samll object heap),LOH(large object heap),JIT Heap,Process Heap关系和区别。也不会着重讨论GC,单论Large Object Heap在.NET 4.5中的改进。

      在比较频繁使用Large Object(对象不小于85,000bytes)系统中,经常容易抛出Out-of-memory的exception。可能我们的物理内存已经不足够使用,然而有时在物理内存足够的情况下,也还是会抛出这种错误,这实际上就是Memory free list中的LOH fragmentation引起的。

      CLR托管对象中有两种不同的内存分配方式,一个就是SOH,另一个就是LOH。实际上我们经常说的Manage Heap其实就是GC heap,可以这样认为GC heap=SOH+LOH。当托管对象声明时,需要在当前AppDomain内存中开辟一块连续的内存空间,SOH与LOH分配方式不同的时,SOH在当前内存空间不够或者找不到合适连续内存块(连续空闲内存空间)会自压缩空间,把不连续的空闲空间整合在一起以满足分配需要,这点类似磁盘的碎片整理。

LOH不会进行这种操作。当前内存指针找不到合适的内存空间时,就是抛出Out-of-memory的exception。LOH对象的生存周期是根据“代次” (generation)来确定的,事实上是最高代龄(Genaration 2),对象越大,对其进行垃圾回收的成本也就越大,基于此考虑,CLR对LOH对象采取这种特殊的管理方式。当前内存不足时,LOH不会进行压缩以获取足够的内存空间,这也是产生fragmentation的根本原因。但是有点特殊的是当连续空间的多个大对象已被标记为可回收时候,CLR会检测并合并这些可用空间,形成更大的连续可用的内存空间,当分配新的Large Obj时,是开辟新的内存空间,还是进行查找合并可用空间呢,CLR会根据实际环境运行情况进行评估,以求在效能和应用上达到平衡。

在.NET 4.5中改进了两点,第一是改进free list的轮检管理机制,从而达到对memory fragments空间的有效利用。当当前内存指针空间不可用的时候,memory allocator会再次遍历memory fragments,查看时候有满足容量的memory fragments,或者合并相邻的可用memory fragments以满足当前对象内存需要。第二在GC Server模式下,CLR会在分配大容量内存和性能之前寻找一个平衡,因为Large Object本身就在第2代,CLR会增加其分配阈值,会获取一个非常大的回收内存。这个是SOH之前的平衡做法有些类似。

多数大对象在本质是相同的,所以我们可以采用object pooling来避免LOH的频繁操作,以达到提高效能的目的。