在GC进行垃圾回收时,多是进行可达性分析来判断对象是否存活,为了尽可能的缩小STW的时间,使用并发的可达性分析算法:三色标记法来并发标记对象存活状态
在内存中,对象之间的引用关系是多对多的,在数据的逻辑结构中属于图的算法,
个人认为,三色标记法就是对对象引用关系图的一个广度优先遍历过程,具体流程:
三色标记法当然有三种颜色;
黑色表示当前对象已经被访问过并且其所有的引用对象也都被访问过了
灰色表示当前对象已经被访问过但是至少还存在一个他引用的对象没有被访问过
白色表示该对象还没有被访问过
黑色对象直接引用白色对象的情况是不被允许的。
步骤:
- 把所有对象都标记为白色;
- 将全局变量和函数栈里的对象置为灰色(GCROOT),从这里开始扫描;
- 如果一个灰色对象的所有引用对象都被扫描完,那么就把该灰色对象变为黑色,然后把他直接引用的对象标记为灰色;再从灰色对象集合依次往下扫描;
- 扫描结束后仍然为白色的对象就是可回收对象
这其实就是一个广度优先遍历图的过程
在并发标记的场景下,也就是用户线程可以操作正在分析的对象的引用关系的情况下,会发生两种情况:
一是原本应该被回收的对象没有被标记为白色,例如已经将该对象标记为黑色或灰色后,用户线程又将其他对象对该对象的引用删除掉了,因为其已经在黑色或灰色集合中了,所以本次不能被回收,但是这种情况问题不大,被称为浮动垃圾,在下次GC即可回收
二是原本不应被回收的对象被标记为白色,这是用户程序不能容忍的情况。
经过分析,当同时满足:对象被灰色对象的所有引用都删除掉且有黑色对象重新对其进行了引用的情况下,才能发生这种情况。
对于这种情况,破坏两个条件的其中一个即可避免:
增量更新:
CMS使用这种策略,当黑色对象重新引用了某些对象时,会将这些黑色对象记录下来,分析结束后重新对记录的有增量引用的黑色对象出发进行增量分析
原始快照:
G1垃圾收集器使用这种策略,当灰色对象删除了对某些对象的引用时,将这些引用关系记录下来,在灰色对象开始bfs搜索时除了当前的引用情况外仍将记录下来删除的引用纳入扫描范围
如上两种方法破坏了对象被误杀的条件,满足了在并发场景下的可达性分析。
在实现方式上,虚拟机通过写屏障(机器码层面)的技术,类似AOP的概念,在操作对象引用关系的时候通过在屏障内增加如上手段来实现。