.NET GC 精要(二)

本文讲述了 .NET GC 的一些细节知识,内容大部分来自于书籍 Under the Hood of .NET Memory Management
(注:本文假设你了解 .NET 的基础知识,譬如值类型,引用类型等)

进阶

之前我们讲述 SOH(Small Object Heap) 时提过其会执行内存压缩过程,但如果程序中存在大量的(小)对象的话,在 SOH 中完整执行一遍内存压缩也会消耗不少的时间;再者在一般的程序运行过程中,大部分对象其实都属于临时对象,创建使用之后便不再被引用了(我们应该及时清理这些对象),而另外那些非临时对象,往往被引用的时间又都很长(我们不需要每次 GC 都去遍历标记他们).

基于此, .NET 又将 SOH 中存储的对象进行了分代处理,用于进一步优化 GC 性能:

  • Generation 0 (Gen 0): 未经历过 GC 检查的对象(新创建对象)
  • Generation 1 (Gen 1): 经历过 1 次 GC 检查并未被清理的对象
  • Generation 2 (Gen 2): 经历过大于等于 2 次 GC 检查并未被清理的对象

以下是一张相关示意图:

在这里插入图片描述

GC 流程大概会在以下条件达成时自动触发执行:

  • 每个分代达到各自不同的内存大小阈值:
    • Gen 0 达到 ~256 K (GC 会进行 Gen 0 回收)
    • Gen 1 达到 ~2 MB (GC 会进行 Gen 1 回收)
    • Gen 2 达到 ~10 MB (GC 会进行 Gen 2 回收)
  • GC.Collect() 被调用(GC 会进行 Gen 2 回收)
  • OS(操作系统)发送了 low memory(内存不足) 通知(GC 会进行 Gen 2 回收)

当然,上面提及的各个分代阈值都只是初始值, .NET 也会根据程序的运行状况动态进行调整.

每个分代的 GC 流程也有不少区别,我们依次来看:

Gen 0 回收

GC 对 Gen 0 区域中的对象进行遍历标记,然后将标记(被引用)的对象移入 Gen 1 分代的存储区域(该对象变为了 Gen 1 对象),对未标记的对象则执行清理操作,最后,Gen 0 区域总是会被清空.

拿上面的示意图举例,经过 Gen 0 回收之后, SOH 内存分布如下所示:

在这里插入图片描述

(Object Y 从 Gen 0 变为了 Gen 1, Object Z 被清理, Gen 0 区域整体被清空)

Gen 1 回收

GC 对 Gen 0 区域和 Gen 1 区域中的对象进行遍历标记,然后将标记对象移入对应的 Gen 1 和 Gen 2 区域,接着执行相关的内存压缩过程,自然 Gen 0 区域最后也会被清空.

仍然拿最初的示意图举例,经过 Gen 1 回收之后, SOH 内存分布如下所示:

在这里插入图片描述

(Object Y 从 Gen 0 变为了 Gen 1, Object Z 被清理, Gen 0 区域整体被清空, Object X 变为了 Gen 2)

Gen 2 回收

经过 Gen 0 回收 和 Gen 1 回收的讲述, Gen 2 回收的流程就可以直接类比了,在此不再赘述(当然, Gen 2 中被标记的对象不会再移入别处,仍然保持在 Gen 2 区域中)

依旧拿最初的示意图举例,经过 Gen 2 回收之后, SOH 内存分布如下所示:

在这里插入图片描述

(Object Y 从 Gen 0 变为了 Gen 1, Object Z 被清理, Gen 0 区域整体被清空, Object X 变为了 Gen 2, Object W 被清理)

未完待续(to be continued)

发布了144 篇原创文章 · 获赞 146 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/tkokof1/article/details/104082887
GC