CLR 垃圾回收器根据所占空间大小划分对象。大对象和小对象的处理方式有很大区别。比
如内存碎片整理 ------ 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器
是如何处理大对象的,大对象对程序性能有哪些潜在的影响。
大对象堆和垃圾回收
在.Net 1.0 和 2.0 中,如果一个对象的大小超过 85000byte,就认为这是一个大对象。
这个数字是根据性能优化的经验得到的。当一个对象申请内存大小达到这个阀值,它就会
被分配到大对象堆上。这意味着什么呢?要理解这个,我们需要理解.Net 垃圾回收机制。
如大多人所知道的,.Net GC 是按照“代”来回收的。程序中的对象共有 3 代,0 代、1
代和 2 代,0 代是最年轻的对象,2 代对象存活的时间最长。GC 按代回收垃圾也是出于性
能考虑的;通常的对象都会在 0 代是被回收。例如,在一个 asp.net 程序中,和每一个请求
相关的对象都应该在请求结束时回收掉。而没有被回收的对象会成为 1 代对象;也就是说 1
代对象是常驻内存对象和马上消亡对象之间的一个缓冲区。
从代的角度看,大对象属于 2 代对象,因为只有在 2 代回收时才会处理大对象。当某
代垃圾回收执行时,会同时执行更年轻代的垃圾回收。比如:当 1 代垃圾回收时会同时回
收 1 代和 0 代的对象,当 2 代垃圾回收时会执行 1 代和 0 代的回收.
代是垃圾回收器区分内存区域的逻辑视图。从物理存储角度看,对象分配在不同的托
管堆上。一个托管堆(managed heap)是垃圾回收器从操作系统申请的内存区(通过调用
windows api VirtualAlloc)。当 CLR 载入内存之后,会初始化两个托管堆,一个大对象堆
(LOH –large object heap)和一个小对象对(SOH – small object heap)。
内存分配请求就是将托管对象放到对应的托管堆上。如果对象的大小小于
85000byte,它会被放置在 SOH;否则会被放在 LOH 上。
对于 SOH,对象在执行一次垃圾回收之后,会进入到下一代。也就是说如果在第一次
执行垃圾回收时,存活下来的对象会进入第二代,如果在第 2 次垃圾回收之后该对象仍然
没有被当作垃圾回收掉,它就会成为 2 代对象;2 代对象就是最老的对象不会在提升代数。
当触发垃圾回收时,垃圾回收器会在小对象堆做碎片整理,将存活下来的对象移动到
一起。而对于大对象堆,由于移动内存的开销很大,CLR 团队选择只是清除它们,将回收
掉的对象组成一个列表,以便满足下次有大对象申请使用内存,相邻的垃圾对象会被合并
成一块空闲的内存块。
需要时时留意的是,直到.Net 4.0 中也不会对大对象堆做碎片整理操作,将来也许会
做。因此如果你要分配大对象并不想他们被移动,你可以使用 fixed 语句。
如下小对象堆 SOH 的回收示意图
评论0
最新资源