2年不用,就忘了,在这记录下。
5.0版本的双色标记清除算法
此算法中,每个对象会有两种标记态:白色和黑色;新创建的对象都是白色
过程:
1.初始化阶段:将root链表中的所有对象放入待检链表中;
2.标记阶段:循环取出待检链表中的对象,标记为黑色,并遍历这个对象关联的其他对象,标记黑色;
3.回收阶段:将白色回收。黑色放回对象链表等待下一次GC
问题:GC无法被打断,标记和回收必须一气呵成。否则新创建的标白可能会被回收,标黑?我没检测就标黑,不行。
5.1版本的三色增量标记清除算法
此算法中,解决了GC无法被打断的问题,GC过程可以被打断;每个对象会有三种标记态:白色,灰色,黑色;新创建的对象都是白色
过程:
1.初始化阶段:将root链表中的所有对象标为灰色,放入灰色链表中;
2.标记阶段:循环取出灰色链表中的对象,标记为黑色,并遍历这个对象关联的其他对象,如果是白色,则标为灰色,放入灰色链表中;否则表为黑色;
3.回收阶段:将白色回收。黑色放回对象链表等待下一次GC
优化:解决了无法被打断的问题,只要灰色链表中还存在元素,这个GC过程就会一直持续下去,即使打断了也没有影响。并且新增对象时,若处于标记阶段,就可以将新的对象标为灰色待检,不会被无情抹掉。
问题:如果处于回收阶段,新创建了对象,仍然会被无情回收。
5.1增强版本 引入“双白色”概念
此算法中,每个对象会有四种标记态:当前白(白1),非当前白(白2),灰色,黑色;新创建的对象视情况标为白1或白2
过程:第N次GC时,使用白1作为要回收的白色,而在此期间新创建的对象统一用白2标记,避免被无情回收。
第N+1次GC时,使用白2作为要回收的白色,而在此期间新创建的对象同意用白1标记,避免被无情回收。
(这次回收白1,新来的都用白2;下次回收白2,新来的都用白1)
优化:解决5.1版本存在的问题
5.2版本之后的分代GC
5.2版本之前的GC都是分步GC,每次GC都全轮一遍标记回收。而从5.2开始,出现了分代模式。可以通过主动调用进行设置。其特点跟C#的GC类似。分为了多代对象,有不同的GC策略,提高了GC性能。
分代GC主要分两个GC过程,一个是完整的GC(即major GC),其二是局部GC(即minor GC)。会根据新增内存量以及回收量决定下一次的GC策略。
状态标识枚举:
G_NEW(0代对象):本次cycle创建的新对象(没有引用任何old对象)
G_SURVIVAL(1代对象):上一轮cycle创建的对象 -- 只活过一轮,下一次如果是白色的话,仍然会被回收。
G_OLD0(1代对象):表示本次cycle创建的新对象,但是引用了old对象
以下是老年代对象:
G_OLD1(2代对象):表示作为老对象第一次存活了整个gc过程 -- 为什么需要G_OLD1(从G_SURVIVAL或者G_OLD0变成G_OLD1,而不是直接从G_SURVIVAL或者G_OLD0变成G_OLD)?因为如果节点A现在是在G_SURVIVAL或者G_OLD0,在同一个cycle中转成old对象前,有一个子节点B引用了A节点(例:A[B]=C),这时候因为A节点仍然不是old,所以不会触发到barrier与barrier_back操作,所以节点B仍然是G_NEW,到最后,A节点直接转变成G_OLD的话,B节点转变成G_SURVIVAL,因为在youngcollection中不会对g->reallyold链表进行markold操作,所以在下一次gc中,如果B节点不可达(即是白色)的话,那么在这次gc中会被释放。虽然可以改成old也传播,但是这样的话就破坏了规则(会对所有的G_OLD都遍历,这样就达不到缩减成本的目的了)。
G_OLD(3代对象):表示真正的old对象,不会被回收 -- 作为老对象需要2次gc过程,作为新创建的对象需要经过3次gc才会到达此阶段。
G_TOUCHED1(3代对象):标记位G_OLD的对象在这次gc barrier_back的状态 -- 新touch的对象,需要进入到grayagain中
G_TOUCHED2(3代对象):标记为G_OLD的对象在上一次gc barrier_back的状态前进到touched2 -- 分代gc结束时,从G_TOUCHED1转成G_TOUCHED2,并设置为黑色,仍然存在于grayagain中,以便下一轮再次有新对象使该对象 barrier_back 时,只需要修改为灰色(使用计算机的局部性原理,这次用到的东西,下一次可能还会触发,避免下次触发时进行链表的操作)。如果下一次没有触发为touched1则变成G_OLD。在correctgraylist中会对touched2跟old的对象从grayagain删除。(参考文章)
时刻保持对代码的敬畏之心,我是一个游戏码农,一个追梦的人。
喜欢就点个关注吧!