GC并发标记的问题

GC并发标记的问题

在最开始学习垃圾回收器并发标记的问题时,理解的不够到位,这里结合马士兵的讲解记录一下(马士兵喜欢讲一半,故弄玄虚😒)

1.垃圾回收器(10种)

java 1.8默认Parallel Scavenge + Parallel Old

垃圾回收器分为

  • 分代模型(左6)
  • 不分代模型(右3)

2. 判断对象能否回收

2.1 引用计数

循环引用对象无法回收,导致内存泄露

2.2 可达性分析

  • JVM中的垃圾回收器通过可达性分析来探索所有存活的对象
  • 扫描堆中的对象,看能否沿着GC Root对象为起点的引用链找到该对象,如果找不到,则表示可以回收
  • 可以作为GC Root的对象
    • 虚拟机栈(栈帧中的本地变量表)中引用的对象。 
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI(即一般说的Native方法)引用的对象

3. 并发标记的问题

3.1 两种情况

  • 一个对象被GC认为不是垃圾,但是随着业务进行,变成垃圾
    • 浮动垃圾 floating garbage
    • 解决方法:下一次GC清除
  • 一个对象被GC认为是垃圾,随着业务进行,变成不是垃圾
    • 缓存

3.2 三色标记法

CMS和G1都是用的三色标记法(基于可达性分析)

3.3 解决方法

情况一

先B->D

后B->D消失

解决:下一次GC清除浮动垃圾

情况二

先B->D

后B->D消失,A->D增加,导致D漏标

CMS的解决方案(Incremental Update):

存在bug

因此CMS垃圾回收器必须要有remark重写标记阶段,从头扫一遍

G1的方案(SATB Snapshot-At-The-Begining)[2]

  • SATB—Snapshot-At-The-Begining
    在G1中,使用的是SATB方式,删除的时候记录所有的对象
    它有3个步骤
    • 在开始标记的时候生成一个快照图,标记存活对象

    • 在并发标记的时候所有被改变的对象入列(在write barrier(写屏障)里把所有旧的的引用所指向的对象都变成非白的)

    • 可能存在浮动垃圾,将在下次被收集

SATB是维持并发GC的一种手段. G1并发的基础就是SATB

SATB可以理解成在GC开始之前对堆内存里的对象做一次快照,此时活的对象就认为是活的,从而形成一个对象图

  • 标记GC过程找那个分配的对象
    在GC收集的时候,新生代的对象也认为是活的对象,除此之外其他不可达的对象都认为是垃圾对象

    如何找到在GC过程中分配的对象呢?每个region记录这两个top-at-mark-start(TAMS)指针, 分别为

    • prevTAMS
    • nextTAMS
      在TAMS以上的对象就是新分配的,因而被视为隐式marked
      通过这种方式我们就找到了在GC过程中新分配的对象,并把这些对象认为是活的的对象
  • 如何解决GC过程中的引用变化
    解决了对象在GC过程中分配的问题,那么在GC过程中引用发生变化的问题是怎么解决呢?

    G1给出的解决办法是通过Write Barrier—写屏障,其作用就是对引用字段进行赋值做了额外的处理

    通过Write Barrier`就可以了解到哪些对象发生了什么样的变化

  • SATB的完整过程
    mark的过程就是遍历Heap标记live Object的过程,采用的是三色标记算法,这三种颜色为
    • white—表示还未访问到
    • gray—访问到但是它用到的引用还没有完全扫描完
    • black—访问到而且其用到的引用已经完全扫描完
    整个三色标记算法就是从GC root是出发遍历Heap, 针对可达到的对象先标记为white为gray,然后再标记gray为black遍历完成之后所有可达的对象都是black的,所有white都是可以回收的
  • 并发标记对象漏标记情形
    SATB仅仅对于在marking开始阶段进行”Snapshot”—mark all reachable at mark start, 但是concurrent的时候并发修改可能造成对象漏标记

    对象漏标记情形可能有如下几种:

    • 对black新引用了一个white对象,然后又从gray对象中删除了对该white对象的引用,这样会造成该white对象的娄标吉

    • 对black新引用了一个white对象,然后从gray对象删除了一个引用该white对象的white对象,这样也会造成改对象的漏标记

    • 对black新引用了一个刚new出来的white对象,没有其他gray对象引用改wihte对象.这样也会造成了该white对象漏标记

    SATB提供的解决办法
    对于三色算法在concurent的时候可能会产品的漏标记问题,SATB在marking阶段中

    • 对于从gray对象移除的目标引用对象标记为gray
    • 对于black引用的新产生的对象标记为black

    由于是在开始的时候进行snapshot, 因而可能存在Floating Garbage

  • 漏标与误标
    误标没什么关系,顶多是造成浮动垃圾,在下次GC还是可以回收的但是漏标的后果是致命的,把本应该存活的对象给回收了,从而影响程序的正确性
    • 漏标
      漏标的情况只会发生在白色对象中,且满足一下任一条件
      • 并发标记时, 应用线程给一个黑色对象的引用类型赋值了该白色类型的引用
        解决办法—利用post-write- barrier写后屏障,记录所有新增的引用关系,然后根据这些引用关系为根重新扫描一遍
      • 并发标记时, 应用线程给删除所有灰色对象到该白色对应的引用
        解决办法—利用pre-write barrier写前屏障,将所有即将被删除的引用关系的旧引用记录下来,最后以这些旧引用为根重新扫描一遍

参考资料


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!