大型对象堆揭秘
Maoni Stephens

CLR 垃圾回收器 (GC) 将对象分为大型、小型两类。如果是大型对象,与其相关的一些属性将比对象较小时显得更为重要。例如,压缩大型对象(将内存复制到堆上的其他位置)的费用相当高。在本月的专栏中,我将深入探讨大型对象堆。我将讨论符合什么条件的对象才能称之为大型对象,如何回收这些大型对象,以及大型对象具备哪些性能意义。

大型对象堆和 GC
® .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 团队会选择扫过所有对象,列出没有被清除的对象列表以供以后重新使用,从而满足大型对象的分配请求。相邻的被清除对象将组成一个自由对象。
有一点必须注意,虽然目前我们不会压缩 LOH,但将来可能会进行压缩。因此,如果您分配了大型对象并希望确保它们不被移动,则应将其固定起来。
请注意,下面的图仅用于说明。我使用了很少的对象,只为说明堆上发生的事件。实际上,还存在许多对象。
图 1 说明了一种情况,在第一次第 0 代 GC 后形成了第 1 代,其中 Obj1 和 Obj3 被清除;在第一次第 1 代 GC 后形成了第 2 代,其中 Obj2 和 Obj5 被清除。
CLR 全面透彻解析:大型对象堆揭秘图 1 SOH 分配和垃圾回收(单击图像可查看大图)

相关文章:

  • 2021-05-18
  • 2021-06-19
  • 2021-05-11
  • 2022-12-23
  • 2021-12-05
  • 2021-12-17
  • 2021-09-04
  • 2021-12-05
猜你喜欢
  • 2022-01-02
  • 2022-12-23
  • 2021-11-14
  • 2021-08-26
  • 2022-01-28
  • 2021-06-07
  • 2021-08-06
相关资源
相似解决方案