三色标记法
这个算法就是把 GC 中的对象划分成三种情况:
白色:还没有搜索过的对象(白色对象会被当成垃圾对象)
灰色:正在搜索的对象
黑色:搜索完成的对象(不会当成垃圾对象,不会被GC)
上面说的这个问题就是可以用下图来表示:
这个图的意思就是:假设有 A -> B -> C, A 是 GC Roots 关联的对象,那么首先会把 GC Roots 标记,也就是 A 标记成灰色(证明现在正在搜索 A 相关的),然后搜索 A 的引用,也就是 B,那么搜索了 B,把 B 变成了灰色,那么 A 就搜索完成了。(此时注意,现在是不管 C 的,因为 C 不是 A 的引用,现在只管 A 的引用是什么)。此时把 A 相关的搜索完了,那么 A 就变成了黑色,证明 A 已经 ok 了。
(Ps:浮动垃圾就是说,此时 A 又不用了,那么 A 是没办法回收的,因为 A 已经标记了)
此时准备要搜索 B 了。
刚好,此时,用户线程要执行了,用户线程把原来 A -> B -> C 的引用改成了 A -> C,同时 B 不再引用C。
然后又到 GC 线程执行了。
GC 线程发现 B 没有引用的对象了(因为用户线程已经把 B -> C 去掉了),那么 B 就相当于搜索完成了,变成黑色了。
最后,C 怎么办,C还是白色的呢,白色的是不会搜索,当做垃圾处理的。
解决办法
此时的解决办法就是有一个叫做写入屏障的东西。就是说,如果A已经被标记了(已经是黑色的了),那么用户线程改动 A->C的时候,会把 C 变成灰色,这样,以后就可以搜索 C了。
例如:
write_barrier(obj,field,newobj){
if(newobj.mark == FALSE){
newobj.mark = TRUE
push(newobj,$mark_stack)
}
*field = newobj
}
这里的意思就是改的时候会 push(newobj,$mark_stack),
那么,通过这样子的一个方式,就能解决这个问题:GC 线程和 用户线程并发的时候,用户线程把失效的对象又至为有效,这个时候怎么处理。
cms 的解决方案 增量更新 Incremental Update
并发线程执行时 a 变为黑色
线程2 在线程1标记完A时 A指向c a会变灰
会产生ABA 问题
所以 cms 的remark 阶段 ,必须从头扫描一遍