C# 底层GC原理和机制

最近经常被问到C#的GC原理,发现自己知之甚少。偶然发现在《CLR via C#》一书中有非常详细的解释,特此做一些整理和记录。

对象的生存周期通常用引用计数来管理,例如Mircosoft自己的COM,但是引用计数的一个明显问题就是没有办法解决循环引用。

C#使用一种叫引用跟踪算法。引用跟踪算法只关心引用类型的变量,因为只有它能够引用堆上的对象,我们将所有引用类型的变量称为

CLR的GC是基于代的垃圾回收器(Generational Garbage Collector),这个垃圾回器基于以下3点假设:

  1. 对象越新,生存期越短
  2. 对象越老,生存期越长
  3. 回收堆的一部分,速度快于回收整个堆

差不多同时分配的对象之间有较强的联系,例如new一个BinaryWriter后者往往在FileStream中使用。

GC分成两个阶段,开始之前CLR会暂停进程中所有的线程,防止检查过程中访问的对象被修改。然后进入第一个阶段,称为标记阶段。CLR遍历所有对象将同步索引字段中的一位置成0,表明所有对象都应该被删除。垃圾回收器从所有活动的根出发开始查找,看它还引用了哪些其它的对象将其标记为1,如果该对象还有其它的引用,则重复该过程,这样便得到一个图的结构,我们称之为可达对象图(Graph of Reachable Object)。第二个阶段,称为压缩阶段。其实更准确的翻译应该是Compact翻译成紧凑,即移除无用对象后,将幸存的对象拷贝到连续的内存空间中,排列整齐。做为压缩(compact)的一部分,CLR还需要将移动后的幸存对象的引用减去相应的偏移,保证根对他们的引用是正确的,而不是null。