CLR全面透彻解析:大型对象堆揭秘2011-10-26 msdn Maoni StephensCLR 垃圾回收器 (GC) 将对象分为大型、小型两类。如果是大型对象,与其相关的一些属性将比对象 较小时显得更为重要。例如,压缩大型对象(将内存复制到堆上的其他位置)的费用相当高。在本月的专 栏中,我将深入探讨大型对象堆。我将讨论符合什么条件的对象才能称之为大型对象,如何回收这些大型 对象,以及大型对象具备哪些性能意义。大型对象堆和 GC在 Microsoft® .NET Framework 1.1 和 2.0 中,如果对象大于或等于 85,000 字节,将被视为大型对象。此数字根据性能优化的结果确定。当对象分配请求传入后,如果符合 该大小阈值,便会将此对象分配给大型对象堆。这究竟是什么意思呢?要理解这些内容,先了解一些关于 .NET 垃圾回收器的基础知识可能会有所帮助。众所周知,.NET 垃圾回收器是分代回收器。它包 含三代:第 0 代、第 1 代和第 2 代。之所以分代,是因为在良好调优的应用程序中,您可以在第 0 代 清除大部分对象。例如,在服务器应用程序中,与每个请求关联的分配将在完成请求后清除。仍存在的分 配请求将转到第 1 代,并在那里进行清除。从本质上讲,第 1 代是新对象区域与生存期较长的对象区域 之间的缓冲区。从分代的角度来说,大型对象属于第 2 代,因为只有在第 2 代回收过程中才能 回收它们。回收一代时,同时也会回收所有前面的代。例如,执行第 1 代垃圾回收时,将同时回收第 1 代和第 0 代。执行第 2 代垃圾回收时,将回收整个堆。因此,第 2 代垃圾回收也称为完整垃圾回收。 在本专栏中,我将使用术语“第 2 代垃圾回收”而不是“完整垃圾回收”,但它 们可以互换。垃圾回收器堆的各代是按逻辑划分的。实际上,对象存在于托管堆栈段上。托管堆 栈段是垃圾回收器通过调用 VirtualAlloc 代表托管代码在操作系统上保留的内存块。加载 CLR 时,将 分配两个初始堆栈段(一个用于小型对象,另一个用于大型对象),我将它们分别称为小型对象堆 (SOH) 和大型对象堆 (LOH)。然后,通过将托管对象置于任一托管堆栈段上来满足分配请求。如果对象 小于 85,000 字节,则将其放在 SOH 段上;否则将其放在 LOH 段上。随着分配到各段上的对象越来越多 ,会以较小块的形式提交这些段。对于 SOH,垃圾回收未处理的对象将进入下一代;由此第 0 代 回收未处理的对象将被视为第 1 代对象,依此类推。但是,最后一代回收未处理的对象仍会被视为最后 一代中的对象。也就是说,第 2 代垃圾回收未处理的对象仍是第 2 代对象;LOH 未处理的对象仍是 LOH 对象(由第 2 代回收)。用户代码只能在第 0 代(小型对象)或 LOH(大型对象)中分配。只有垃圾回 收器可以在第 1 代(通过提升第 0 代回收未处理的对象)和第 2 代(通过提升第 1 代和第 2 代回收 未处理的对象)中“分配”对象。触发垃圾回收后,垃圾回收器将寻找存在的对象并 将它们压缩。不过对于 LOH,由于压缩费用很高,CLR 团队会选择扫过所有对象,列出没有被清除的对象 列表以供以后重新使用,从而满足大型对象的分配请求。相邻的被清除对象将组成一个自由对象。