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的频繁操作,以达到提高效能的目的。