ZGC出现的背景

STW:垃圾回收会暂停所有的业务线程
带来的问题:
手机系统卡顿Android系统卡顿
最大的问题就是卡顿,现在已经控制在几个ms级别

证券交易系统的实时性
大数据平台

垃圾回收器的发展

内存方便———>原来越大

线程方面———-> 单线程———-多线程

STW方面——-> 越来越小

ZGC 介绍

背景: jdk 11 开始
目标 :
停顿10ms内
停顿不会随对象增多二增加
支持8M - 4T 的大内存

ZGC的内存布局

同G1,但不是同一大小的region
三种页面: 大,中,小
image.png
小页面优先回收,中页面,大页面,尽量不收

为什么这么设计内存?

Linux kernel 2.6引入了大页内存 取代4kb的内存
Huge pages 两种格式 2m, 1G 默认2M

NUMA架构

多核CPU 读不同区域的内存

ZGC的核心概念

指针着色技术

条件:64位机器上才可以
ZGC 中低42位表示堆空间
借高位做指针标识,去做 并发标记,转移,重定位
image.png

ZGC的流程

一次ZGC流程
标记阶段(标识垃圾)
转移阶段(对象复制或移动)
image.png

根可达算法

从GC root对象作为起点,搜索走过的路径:引用链,打不到就是不可用
image.png
作为根的四种对象:

  1. 虚拟机栈:栈中的本地变量表:用到的参数,局部变量,临时变量等
  2. 方法区中类静态变量:静态变量
  3. 方法区中常量,:字符串常量池的引用
  4. 本地方法栈中的JNI指针 ,一般是指Native方法

初始标记:从根集合(gc roots )出发,找出根集合引用的活跃对象
并发标记:根据初始标记找到的根对象,使用深度遍历对象的成员变量进行标记

并发标记算法

image.png

  1. 初始标记 :STW,扫描所有GC roots,处理时间与GCRoot数量正相关,STW时间不与堆大小,对象数量相关
  2. 并发标记 : NO-STW,扫描剩余所有对象,处理时间较长,并发,业务线程与GC线程,漏标问题存在
  3. 再标记:STW,处理漏标的对象,SATB算法解决

    并发转移算法

    转移阶段
  • 并发转移准备:分析最优价值的GC分页 无STW
  • 初始转移:转移初始标记的活对象,同时做对象重定位  有STW
  • 并发转移:对转移并发标记存活的对象转移,无STW

转发表:记录对象新旧地址的表
对象的转移个转发表是一个事务

重定位算法

此处用到了颜色指针:指针着色中M0,M1区分是哪次的GC对象
并发标记对象的重定位:
下次的GC并发标记,做上次并发转移对象的重定位
image.png

ZGC的读屏障

涉及的对象:并发转移,但没有对象重定位的对象
触发时机:在两次GC之间业务线程访问了这种对象
触发读屏障的操作:对象重定位+删转发表记录
?原因是对象发生了改变,但没做指针修改

读屏障技术实现:
相当于AOP,
读屏障是JVM向应用代码插入一小段代码的技术。
当从堆中读引用时,就需要加入一个Load Barrier(读屏障)
判断当前指针是否Bad Color(不是本次GC的Mark颜色)
修正指针:对象重定位+删除转发表记录

后续

触发机制 JAVA16

预热规则: 日志关键字:Warmup

基于分配速度的自适应算法:
最主要的触发GC方式,
原理:ZGC根据近期的对象分配速率,以及GC时间,计算当前内存占用达到什么阈值
基于固定时间间隔
主动触发规则
阻塞内存分配请求触发
外部触发
元数据分配触发

参数设置

ZGC 优势不仅在于其超低的 STW 停顿,也在于其参数的简单,绝大部分生产场景都可以自适应。当然,极端情况下,还是有可能需要对 ZGC 个别参数做个调整,大致可以分为三类:

  • 堆大小:
  • Xmx。当分配速率过高,超过回收速率,造成堆内存不够时,会触发 Allocation Stall,这类 Stall 会减缓当前的用户线程。因此,当我们在 GC 日志中看到 Allocation Stall,通常可以认为堆空间偏小或者 concurrent gc threads 数偏小。
  • GC 触发时机:
  • ZAllocationSpikeTolerance, ZCollectionInterval。ZAllocationSpikeTolerance 用来估算当前的堆内存分配速率,在当前剩余的堆内存下,ZAllocationSpikeTolerance 越大,估算的达到 OOM 的时间越快,ZGC 就会更早地进行触发 GC。ZCollectionInterval 用来指定 GC 发生的间隔,以秒为单位触发 GC。
  • GC 线程:
  • ParallelGCThreads, ConcGCThreads。ParallelGCThreads 是设置 STW 任务的 GC 线程数目,默认为 CPU 个数的 60%;ConcGCThreads 是并发阶段 GC 线程的数目,默认为 CPU 个数的 12.5%。增加 GC 线程数目,可以加快 GC 完成任务,减少各个阶段的时间,但也会增加 CPU 的抢占开销,可根据生产情况调整。

由上可以看出 ZGC 需要调整的参数十分简单,通常设置 Xmx 即可满足业务的需求,大大减轻 Java 开发者的负担。

应用场景

  • 超大堆应用。超大堆(百 G 以上)下,CMS 或者 G1 如果发生 Full GC,停顿会在分钟级别,可能会造成业务的终端,强烈推荐使用 ZGC。
  • 当业务应用需要提供高服务级别协议(Service Level Agreement,SLA),例如 99.99% 的响应时间不能超过 100ms,此类应用无论堆大小,均推荐采用低停顿的 ZGC。

注意事项

  1. RSS 内存异常现象
  2. 共享内存调整
  3. mmap 节点上限调整

问题与改进