目录
六、 为什么CMS采用增量更新而G1使用原始快照?有什么区别?
一、CMS和G1垃圾回收过程
CMS:开始标记(会STW)——>并发标记(与用户线程并行)——>重新标记(会STW)——>并发清除
G1: 开始标记(会STW)——>并发标记(与用户线程并行)——>最终标记(会STW)——>筛选回收
二、什么是三色标记
1、白色:还没有被垃圾回收器标记的对象
2、灰色:已经被垃圾回收器标记的对象,但至少还有一个直接引用没有被标记
3、黑色:所有引用都已经被垃圾回收器标记的对象
三、标记过程
1、最开始除root根对象是黑色外,所有对象都是白色的
2、将根直接引用的对象标记为灰色
3、将灰色对象能直接引用到的白色对象标记为灰色,将其本身标记为黑色
4、将没有再被引用的灰色对象标记为黑色
5、重复上面过程直到没有灰色
如上图所示,标记之后依然是白色的对象D就是需要被回收的垃圾对象
四、并发标记产生的问题
由于并发标记是与用户线程并行的,所以在并发标记的过程中对象的引用是可能发生变化的,所以可能会产生多标和漏标。并且重新标记为了减少STW的时间不会再标记黑色对象,而是扫描灰色对象的直接引用
多标:会导致产生浮动垃圾,需要在下一次判断引用再回收,无大碍
漏标:会导致不应该被回收的对象被回收,问题严重
如上图:在并发标记的过程中,同时产生这两种情况时就会发生回收错误问题:A和C断开了引用,A又引用了D。
对于对象C:应该回收的对象现在是黑色,留了下来
对于对象D:被引用了但还是白色,由于重新标记时不会再扫描黑色对象,这样会导致对象D被当作垃圾而回收,产生严重bug
五、写屏障
JVM通过写屏障来解决并发标记过程中产生的漏标问题。就是在写操作的前后做一些事情,类似AOP原理
建立引用和删除引用对应代码其实就是一个赋值的写操作:
public static void main(String[] args) {
A a = new A();
A b = null;
b = a;//建立引用
b = null;//删除引用
}
上面说过:同时发生A和C断开了引用,A又引用了D时会有回收错误问题,所以我们只需要破坏其中一种情况即可。
CMS使用增量更新方式:如果要gc的对象又被重新引用了,在建立引用之后把新对象记录下来,然后把引用了新对象的节点变成灰色
G1原始快照方式:当删除引用关系时,在删除引用关系之前做快照,将发生变化的引用关系记录下来,对比变化后重新标记引用链,并把要删除的对象标记为黑色。如果最终这次真的删除了引用关系且没有新的引用,那就做为浮动垃圾下次再标记回收也可以,但这次不能误删
六、 为什么CMS采用增量更新而G1使用原始快照?有什么区别?
1、增量更新是把引用了新对象的对象标记为灰色,重新标记时会再次扫描,过程慢,但不会产生浮动垃圾,cms是一整块old区,慢点也没关系
2、原始快照把要删除的对象标记为黑色,重新标记时不会再扫描黑色,过程快,但是代价是会产生浮动垃圾。G1是将整个堆划分为多个小区域,需要快速清理垃圾