三色标记法及跨代引用

前言
垃圾回收算法有标记整理、标记清除、复制等。但是垃圾怎么标记处理?这就涉及到标记算法,下面会仔细讲解三色标记算法
三色标记法
什么是三色标记法?
垃圾回收过程大致如下:
- 第一步:根据标记算法找到对应的待回收对象
- 第二步:根据对应的回收算法如标记整理、标记清楚等来进行回收已标记好的待回收对象
那对象是怎么标记的?下面继续了解
GC Root过程中,按"是否访问过"标记为三个颜色:
- 白色:未访问过
- 黑色:本对象已访问过,并其引用对象也全部访问过
- 灰色:本对象已访问过,并其引用对象未全部访问过
标记过程是怎么样的?(动态图如下)
- 第一步:根据颜色分为三个集合:白色集合、灰色集合、黑色集合
- 第二步:从上到下进行标记,先标记A,A是尾节点,标记为黑色。并把A从白色集合中移除
- 第三步:标记到D,发现D引用对象还未被标记,先把D标记为灰色。并把D从白色集合中移除
- 第四步:标记到E,发现D引用对象已全部被标记,把D标记为黑色。并把D从灰色集合移除,E从白色集合移除
- 继续上面的类似过程,直到不可达为止
- 最终标记剩:B、C、H为白色不可达,需要被回收

上面标记需要解决什么问题?并发过程中的多标、漏标
标记过程中,如果STW的话,那标记是准确的,但也会造成长期阻塞。所以很多垃圾回收器如CMS会引入并发标记的环节,并发标记过程中对象引用是可变化的,很容易出现多标、漏标情况。
什么是多标?
假设上图标记的的时候D指向E,但标记完之后D释放了E的引用,这时候E其实属于没引用的对象,是可回收的。这种就属于多标。问题也不大,在下次回收的时候回去就行了。
```````
objD.objE = objE;
objD.objE = null;
```````
什么是漏标?
假设上图标记的D指向E,然后E本来指向了G,但是标记过程中,标记前E断开了G的连接,但是会标记成G这时候没引用(变成待回收状态),这时候D引用G,G是有引用的不可回收。但是又被标记成可回收,把正在使用的对象回收掉是会对系统造成影响的,所以不能接受的。
```````
objG = objE.objG;
objE.objG = null;  // 灰色E 断开引用 白色G
objD.objG = objG;  // 黑色D 引用 白色G
```````
漏标需要满足的条件
- 条件一:灰色断开白色对象引用(E断开G)
- 条件二:黑色重新引用白色对象(D引用G)
需要破坏上面其中一个条件来避免漏标情况
怎么破坏漏标的情况来避免漏标?
把并发标记过程中的漏标数据记录下来,然后再重新标记阶段进行重新标记(重新标记是STW,状态安全不可变化)
为什么重新标记就快?
因为并发标记过程中把大部分的对象都标记过了,重新标记只是修正标记过程的极少数漏标对象
怎么把漏标数据记录下来?
有两种:写屏障、读屏障
- 第一种:写屏障。会拦截如下两步其中一步骤
```
objE.objG = null;// 写屏障 + SATB,G1回收器做法
objD.objG = objG;// 写屏障 + 增量更新,CMS回收期做法
```
写屏障 + SATB( 破坏了条件一:【灰色断开白色对象引用】,从而保证了不会漏标 )        -- 保存原始快照,G1用
拦截objE.objG = null,在置为null前,把objE.objG的原来引用对象记录下来。
伪代码为:
```
// 先记录下旧值原来的引用
oldValue = objE.objG
remarkSet.add(oldValue引用对象)
objE.objG = null
```
写屏障 + 增量更新(破坏了条件二:【黑色重新引用白色对象】,从而保证了不会漏标)        -- 增量更新,GMS用
拦截objD.objG = objG,记录最新的引用对象
伪代码为:
```
objD.objG = objG
remarkSet.add(objD.objG引用对象)
```
- 第二种:读屏障                    -- ZGC用
当读取对象时候,一律记录下来。如objG = objE.objG,先把objG记录下来先
实际中垃圾回收期是怎么进行回收的?举G1作为例子
JVM设计了CardTable,将Old区分为一个个card。如果一个card中 有对象指向young区,则标记为 Dirty Card
G1采用 写屏障 + SATB 方式来应对漏标,其步骤如下:
- 第一步:标记开始时生成一个快照标记存活对象
- 第二步:断开引用后,把引用推到GC堆栈,确保下次能被扫描
- 第三步:配合RSet去扫描哪些Region引用到当前标为白色对象,若没有引用则回收
SATB效率高于增量更新原因?
SATB只需要在重新标记时扫描那些被推到堆栈的引用,并配合RSet来判断当前对象是否被引用来进行回收
备注:CSet(一组可被回收Region)、RSet(每个Region有一个RSet,用来 记录其他Region到本Region引用信息
上面提及到card,那我们简单描述一下jvm中card是怎么区分新老年代的
老年代什么情况下会引用新生代引用?
理想情况下,新老年代是相互隔离不会被引用的 。但实际上总会有一个对象出现跨代引用
跨代引用如何产生
- 第一种:GC线程把对象A从年轻代移到老年代
- 第二种:A对象本身是老年代,线程修改了A的引用指向年轻代的B(年轻代到老年代的跨代引用只有第二种情况)
JVM怎么处理跨代引用?
采用分治,将老年代分为多个card,card再GC root的时候被修改,会被标记 Dirty Card(标记的时候如果重新标记为可回收,就会移除dirty card标识)
然后新生代GC的时候会扫描Dirty Card对应的记录了跨代的引用(Card Marking),判断是否还有引用,如果还没有引用就被回收。
总之就是扫描的时候对于标记了跨代引用的Dirty Card记录不会处理,留给新生代处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值