过程

将对象分为三种状态:白色、灰色和黑色。

白色:该对象没有被标记过。
灰色:该对象已经被标记过了,但该对象的引用对象还没标记完。
黑色:该对象已经被标记过了,并且他的全部引用对象也都标记完了。

  1. 初始标记(Initial Marking)
    • 遍历所有的根对象,将根对象和直接引用的对象标记为灰色。在这个阶段中,垃圾回收器只会扫描被直接或者间接引用的对象,而不会扫描整个堆。因此,初始标记阶段的时间比较短。
    • 需要 STW
  2. 并发标记(Concurrent Marking)
    • 在这个过程中,垃圾回收器会从灰色对象开始遍历整个对象图,将被引用的对象标记为灰色,并将已经遍历过的对象标记为黑色。并发标记过程中,应用程序线程可能会修改对象图,因此垃圾回收器需要使用写屏障(Write Barrier)技术来保证并发标记的正确性。
    • 不要 STW
  3. 重新标记(Remark)
    • 矫正并发标记阶段出现的漏标
    • 重新标记的主要作用是标记在并发标记阶段中被修改的对象以及未被遍历到的对象。这个过程中,垃圾回收器会从灰色对象重新开始遍历对象图,将被引用的对象标记为灰色,并将已经遍历过的对象标记为黑色。
    • 需要 STW
  4. 并发清除
    • 将已经确定为垃圾的对象清除掉
    • 不需要STW

多标

导致内存产生浮动垃圾,但好在其可以再下次 GC 的时候被回收,因此问题还不算很严重。

漏标

  • CMS增量更新
    • 既然有黑色对象在自己标记后,又重新指向了白色对象。那么我就把这个黑色对象的引用记录下来,在后续「重新标记」阶段再以这个黑色对象为根,对其引用进行重新扫描。通过这种方式,被黑色对象引用的白色对象就会变成灰色,从而变为存活状态。
  • G1原始快照
    • 既然灰色对象在扫描完成前删除了对白色对象的引用,那么我是否能在灰色对象取消引用之前,先将灰色对象引用的白色对象记录下来。随后在「重新标记」阶段再以白色对象为根,对它的引用进行扫描,从而避免了漏标的问题。通过这种方式,原本漏标的对象就会被重新扫描变成灰色,从而变为存活状态。

See Also