标记-清除算法
go和lua虚拟机以及jvm的CMS和G1垃圾回收器的回收算法的思想均来自于标记-清除算法(Mark-Sweep),它们的gc有重要的两部分:
1.从根节点遍历所有对象,如果可达到,则标记
2.遍历所有对象,如果没有标记,则destory对象
双色标记法
在lua5.1之前,lua采用的是二色标记法,
第一遍扫描对象,并标记扫描到的对象为黑色,第二遍时候将白色对象destory掉
这个过程没法中断,如果在gc的时候创建完新的对象,那么在最后destory清算的时候,这个白色对象显然是没法判断是无法到达的还是新创建出来的
于是为了防止创建的新的白色对象对gc过程产生干扰,干脆就让程序停下来,就不会产生干扰了,但是代价是程序停顿,影响主线程,显然这种方式和不适用了
于是后面有了更先进的三色标记法
三色标记法
在lua5.1之后和go语言以及jvm的CMS和G1垃圾回收器使用的是三色标记法
下面给出它的简易回收原理
三色:
白色:对象没有被垃圾回收器访问时候标记为白色
灰色:这个对象正处在被扫描,但是它所有引用都没访问完的时候被标记为灰色
黑色:当一个对象的所有引用都被扫描了以后的时候,这个对象被标记为黑色
标记过程:
1.从根集开始遍历
2.遍历到的白色对象push入栈并标黑,遍历到灰色和黑色对象则跳过
3.从栈中pop出一个灰色对象,标记为黑色,并将这个节点执行步骤2
4.栈空的时候,将白色对象都destory
实例解析:
当开始的时候,根节点为黑色,可见,B和D对象是不可达到的位置
从根节点遍历,遍历到C,将C标记为灰色,开始遍历C
遍历到E,将其变为灰色,开始遍历E,但是没有可遍历对象,就将其变为黑色
同理也将F变为黑色
C的所有节点遍历完成,将C变为黑色
当没有灰色对象的时候(当灰色对象栈清空的时候),将白色对象destory
当这样就完成了gc
注意到和双色标记法不同的是,这次判断条件是当灰色对象没有的时候进行gc,那时候的白色对象就一定是可以安全回收的对象了
三色标记法的隐患
在扫描B的节点的时候,B节点变为灰色,当扫描B的其他节点时候
C在这时候引用变为A,那么B扫不上C,自己变黑,但是A已经不会再次扫描,C就被误当为“不可达节点”给destory掉,显然是不对的
这个问题的核心点有两个
1.灰色和白色断开
2.黑色和白色建立引用
为了解决上面的问题,在java的G1回收器和CMS垃圾回收器中分别从1和2两个方面入手给了两种解决方案:
G1的方法叫做原始快照(SATB)算法
原始快照是将灰色对象断开到白色对象的引用关系记录下来,扫描结束后根据记录以记录的灰色对象为根重新扫描一次
CMS的增量更新(Incremental Update)算法
增量更新是将黑色和白色建立引用的过程记录下来,根据这些记录以被记录的黑色对象为根重新扫描一次,用的是写屏障技术(write barrier)