前言
介绍三色标记法前,先了解G1回收器的两个概念:
CSet:也叫Collection Set,这个set中装着需要被回收的region。
RSet:也叫Remembered Set,每个region都有一个RSet,记录着有哪些region中有引用指向本
三色标记法
既然是三色标记法,顾名思义,就是用三种颜色来标记内存中的对象,这三种颜色表示不同的意义。
下面就是内存中的对象可能出现的三种颜色,以及它们表示的意义:
黑色:对象本身与其成员变量均已标记完成。
灰色:对象本身被标记了,但是它的成员变量还没有被标记完成
白色:未被标记的对象。
下面用张图直观的表示一下:
上面这张图中,B和C都是A的成员变量,D是B的成员变量。
由于A和C的本身以及它们的成员变量都被标记过了,所以它们自己就被标记为黑色。
而由于D是未被标记的白色,B只能是被部分标记的灰色。
用这种三色标记法,就可以保证,遍历完对象树之后,所有被标上颜色的对象,都是存活对象。
但是三色标记法也有其问题所在。
三色标记法的问题与解决办法
三色标记法就发生在CMS和G1的Mixed GC的并发标记阶段。
所以三色标记法没法解决引用关系发生改变的情况。
比如漏标问题,我们继续拿上面的图举例子。
如果发生了B对D的引用转移到了A中,就会成为下面这种情况。
由于A的新成员变量D是未被标记的白色,而理应是灰色的A已经被标成了黑色,这就会导致整个三色标记法规则的崩溃。
为了解决三色标记法的这种问题,CMS与Mixed GC选择了下面这两种不同的方式:
incremental update
增加了新引用的对象会被重新标记为灰色。
在重新标记阶段对这个对象重新进行扫描。
拿上面的例子来说,如果A新增加了一个指向D的引用,那么就把A重新标记为灰色,然后等重新标记阶段对A的所有引用(A->B、A->C、A->D)进行扫描。
这是CMS采用的解决方案。问题就在于,这个解决方法的效率并不高,仅仅由于新增了一个新的引用,就要把该对象所有的引用都扫描一遍。
因此提出了下面这种解决方案。
SATB
全称为Snapshot At The Beginning。
这个方法有三个步骤:
1、在开始标记的时候,会生成一个所有对象的快照图。
2、在并发标记的过程中,如果发生了引用消失的情况,就将这个引用存到一个堆栈中。
3、在重新标记阶段,会对这个堆栈中的所有引用进行扫描。
这种方法与之前提到过的RSet浑然天成。
在重新标记阶段,只需要扫描消失引用所指向的对象的RSet就能确定,这个对象有没有被别的引用指向了。
而G1中的Mixed GC就采用了这种方法来进行标记。