讲具体的底层算法前,先说明一个概念–三色标记
一、三色标记
根据可达性分析,将“是否访问过”作为条件标记成以下三种颜色:
- 黑色:表示对象已经被垃圾收集器访问过,而且这个对象的所有引用都已经扫描过。黑色的对象代表已经扫描过,它是安全存活的,如果有其他对象引用指向了黑色对象,无序重新扫描一遍。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
- 灰色:表示对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用未被扫描过。
- 白色:表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段,所有的对象都是白色的,若在分析结束的阶段,对象仍然是白色的,说明该对象没有被引用,即不可达。
二、漏标处理办法
漏标会导致被引用的对象被当成垃圾误删除,这是严重的BUG,必须解决。解决办法主要有两种:增量更新、原始快照
1、增量更新
增量更新就是当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中黑色的对象为gc roots,重新扫描一次。这可以简化理解为,黑色对象一旦插入了指向白色对象的引用之后,它的颜色就变成灰色的了。
2、原始快照
原始快照就是当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,再并发扫描结束之后,再将这些记录过的引用关系中灰色对象为gc roots重新扫描一遍,这样就能扫描到白色的对象,将白色的对象直接标记为黑色(目的就是让这种对象在本轮GC清理中能存活下来,待下一轮GC的时候重新扫描,这个对象也有可能是浮动垃圾)
以上无论是对引用关系的记录还是删除,虚拟机的记录操作都是通过写屏障来实现的
三、读写屏障
1、写屏障
写屏障是基于类似AOP的思想实现的,大体上就是在新增对象的引用之后、置空对象的引用之前添加一个方法来记录原来的操作数据,以便重新标记的时候来处理问题。
2、读屏障
和写屏障的原理类似,读屏障是针对第一步,当读取成员变量的时候一律记录下来,以便之后处理问题。
四、记忆集与卡表
这种算法主要是处理可达性扫描时出现的跨代引用的问题
在新生代引入记录集(Remember Set)的数据结构(记录从非收集区到收集区的指针集合),避免把整个老年代加入GCRoots的扫描范围。