浅谈Java垃圾回收器之二

一、G1回收器

1.1、G1的缺陷

空间上,支持部分回收,为了进行部分回收,G1实现了RSet管理对象的引用关系,内存利用率不高,通常引用关系的处理需要额外消耗内存,一般占整个内存的1%~20%左右;
时间上,支持几十个GB或者上百个GB,不能支持特大内存,特别是在内存容量高于100GB的系统中,会因内存过大而导致停顿时间增长;内存过大的话,对象复制更加耗时,G1中的STW停顿时间主要来自垃圾回收(YGC和混合回收)阶段中的复制算法,在复制算法中,需要把对象转移到新的空间中,并且更新其他对象到这个对象的引用。实际中对象的转移涉及内存的分配和对象成员变量的复制,而对象成员变量的复制是非常耗时的。在G1中对象的转移都是在STW中并行执行的,而ZGC就是把对象的转移也并发执行,从而满足停顿时间在10ms以下。

二、ZGC回收器

2.1、概述

JDK11;不支持32位,支持64位OS;停顿10ms以内;理论上最大支持16TB超大内存。

2.2、分区模型

三种大小页面组成堆空间:

  • 小页面(2M):存放的对象大小<256K
  • 中页面(32M):存放的对象大小256K~4M
  • 大页面(>32M):存放的对象大小>4M

2.3、着色指针

优点:相对于G1等传统垃圾回收器在堆空间的每个对象头中处理的效率更高,ZGC不去堆中只在地址引用占几位,寄存器访问更快。

  • 蓝色:初始化过,未被GC回收器扫到
  • 绿色:被扫到(过程M0,第n个GC周期)
  • 红色:被扫到(过程M1,第n+1个GC周期)

绿色和红色互相代表本次和上次GC的标记色。
在这里插入图片描述

2.4、过程

  1. STW初始标记(与roots直连对象)
  2. 并发标记(三色标记)/重定位
  3. STW再标记(处理漏标、SATB(读屏障+多视图映射))
  4. 并发转移准备(筛选分区)
  5. STW初始转移(与roots直连对象)
  6. 并发转移(转发表、读屏障)

在这里插入图片描述

2.4.1、初始标记

假设存在如下图对象之间的引用关系:
ABC蓝

最开始对象ABC的引用指针都蓝。Ps,若存在垃圾Z,则无引用,更无颜色之说。
初始标记阶段会扫描根GC Roots(不在堆里)到其直连的对象A之间的引用,将其到A的引用置为绿色。

一般而言,这类对象比较有限比较少,所以STW很短在1ms内,且STW时长跟堆大小无关,只跟GC Roots对象的多少成正相关。
此时引用如下:
A绿BC蓝

2.4.2、并发标记/重定位

并发标记阶段对每个直连roots的对象A1、A2等等,标记从A1A2开始的所有引用对象,实际中会很多B1B2C1C2等。同样ZGC的并发标记阶段同CMS和G1一样,也使用三色标记的深度算法。

使用三色标记同样也会有漏标的问题,ZGC处理漏标,同G1类似也是通过堆栈快照SATB(snapshot-at-the-begining),只是ZGC对SATB的实现是基于读屏障+多视图映射
读屏障(不是每个对象都加)类似于AOP,遇到读引用的代码,插入beforeAOP操作,将改变的引用关系存下来。比如:
漏标
时刻1:A.name=null,三色标记中A已经是黑色;
时刻2:A.name=C,对黑色的A对象进行类似属性读取的操作叫做读引用,此时做beforeAOP操作存下引用的改变,等待接下来再标记阶段处理可能漏标的C。
Ps:G1采用的写屏障(读比写效率高)
此时,若没有漏标(最乐观)ABC都绿,但可能C漏标所以AB绿C还蓝。如下,
C漏标

2.4.3、再标记

再标记阶段处理漏标,STW一般在1ms内。对可能是蓝色的漏标的C置为绿色。此时ABC都绿,如下,
再标记

2.4.4、并发转移准备

并发转移准备阶段统计哪些页面要回收或不要回收,作用类似于G1的筛选回收阶段。

进行分区中的筛选(有垃圾的,有对象的页面),进行选择性分区回收。比如,该阶段会筛选出页面B不需要回收,而页面A需要回收。

2.4.5、初始转移

初始转移阶段会STW,将与GC Roots直连(“初始”标记一样的意义)的对象转移到另外的分区即页面。

将A挪到页面B,同时A的引用地址改变,所以新引用的颜色变为蓝色。这类对象同样比较有限,所以STW很短在1ms内,且STW时长跟堆大小无关,只跟GC Roots对象多少有关。
此时A蓝BC绿,如下,
在这里插入图片描述

2.4.6、并发转移

并发转移阶段处理非GC Roots直连对象的转移,比如上图中对象A引用的BC等对象,将其转移到页面B。

但是因为该阶段是并发的操作,业务线程使用的BC的引用指针仍旧是绿色的旧的指针,只是BC对象本身被转移到了页面B。
所以需要借助页面A中的转发表存放BC对象的新旧地址,才能让业务线程找到BC新的地址。如下,
并发转移

并且只有当触发了BC的读引用或读屏障后,通过转发表找到新地址,修复BC的旧的绿色的指针,并在转发表中删除已修正的对象引用信息。读屏障代码大约4%性能开销。

那么如果BC对象没有被业务线程读取,触发不到读屏障,ZGC是怎么处理的?
这种情况下,开始下一GC周期的初始标记阶段,新的GC周期使用标记色红色,对页面B中的A对象引用标记为红色,然后进入并发标记/对象重定位阶段,ZGC对页面A中上一次GC未修正的绿色的指针,进行修正,即在页面B中,将BC的引用颜色也置为红色,同时会从页面A中的转发表里删除BC对象的信息。如下,
在这里插入图片描述

为什么着色指针需要绿色和红色两种标记色,因为本次GC需要修正上次GC未处理的引用,着色指针需要相邻两次GC周期的两种不同标记色以作区分,作用等同于YGC复制算法中的两块Survivor区。

2.5、总结

【ZGC的优势】:

  • 多阶段并发,STW短,GC快
  • 调优参数少调优方便
  • 适合越来越大的堆内存

【ZGC快的原因】:

  • 着色指针的使用
  • 三种大小页面的分区模型
  • 复制算法无碎片,清理效率高
  • 三次STW处理的对象数量很有限
  • 相比上一代G1,ZGC将转移也并发了

三、更多拓展

http://www.javashuo.com/article/p-gciijfqg-nw.html
https://zhuanlan.51cto.com/art/202011/632192.htm
https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html

猜你喜欢

转载自blog.csdn.net/songzehao/article/details/121626259