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—访问到而且其用到的引用已经完全扫描完
并发标记对象漏标记情形
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 协议 ,转载请注明出处!