垃圾回收之并发情况下如何确定对象可达

垃圾回收之并发情况下如何确定对象可达

首先介绍下三色标记法:JVM中是通过三色标记法来确定哪些对象需要被回收,把遍历对象图过程中遇到的对象,按照“是否访问过”这个条件标记成一下三种颜色:

  • 白色
    表示对象尚未被垃圾收集器访问过,显然在垃圾收集刚开始,所有对象都是白色的。
  • 黑色
    表示对象已经被访问过,并且该对象的所有的 直接引用到的对象 都被访问过。
  • 灰色
    表示对象已经被访问过,但是该对象 至少有一个直接引用对象 没有被访问到。(简单来说,就是垃圾收集器还没有遍历完这个对象的所有直接引用的对象)

开始讲本文之前,先举个并发标记的例子给大家说明一下:

  • 1
graph LR A[root 黑] -->B(2 - 灰) B --> C(3 - 白)
  • 2
graph LR A[root 黑] -->B(2 - 灰) B --> |XX|C A --> C(3 - 白)

如上图:

  • 1
    并发标记阶段,GC root一开始都是黑色的,因为在初始标记阶段已经标记了root能直接关联的所有对象,所以在并发阶段开始时,root对象都是黑色的,而root对象直接关联的对象是 灰色 的。
  • 2
    在这一步,垃圾回收器还没来的及扫描 2 号灰结点的所有直接引用的结点,用户线程此时执行:
    root黑.field3白 = 2灰.field3白; 
    2灰.field3白 = null;  // 灰色 断开引用 白色

那么转到GC线程执行时,此时3号结点因为只有root结点引用他,但是root结点已经是黑色的了,所以不会再扫描root结点的直接引用结点了,那么3号结点将一直是白色,在回收阶段3号结点将会被 回收,按道理来说,3号结点不应该被回收,因为他还在被root结点引用着,所以这将会造成一个致命的问题。

我们可以看下造成这种问题的必要条件为:

  • 插入了一条或多条从黑色对象到白色对象的新引用;
  • 删除了全部从灰色对象到白色对象的直接或间接引用;

因此我们要解决上述问题,只要破坏上面两个必要条件中的任意一个即可,JVM实现了两种解决方案:增量更新(Incremental Update,CMS使用这种)和原始快照(Snapshot at the begin,即SATB;G1使用这种)。

  • 1
    增量更新破坏的是上面的第一个条件:当黑色对象插入新的指向白色对象的引用时,就把这个新的引用记录下来,等并发扫描结束之后,再把先前记录中的黑色对象拿出来为跟重新扫描一遍,相当于黑色对象插入新的引用后他自身就变灰了,那么也就不存在条件一中的 黑色对象

  • 2
    SATB破坏的时上面的第二个条件,当灰色对象要删除指向白色对象的引用时,就将这个要删除的引用记录下来作为快照,在并发扫描结束后,再将这些快照拿出来,将其灰色对象为根重新扫描一遍,扫描效果相当于没有删除白色对象的引用一样,自然就破坏了条件二中的 删除了全部从灰色对象到白色对象的直接或间接引用

posted @ 2022-03-02 22:30  码出地球  阅读(40)  评论(0编辑  收藏  举报