1 低延迟高可用Java服务的痛点

对于低延迟和高可用的Java服务,GC停顿一直是它们的痛点。
例如一个服务,被要求在100ms内要返回结果,服务可用性要求为99.99%。假设服务收到的请求速率稳定, GC的平均停顿时间为50ms,每分钟GC 5次, 服务的平均响应耗时为60ms,则0.4%(5*50ms/60000ms)的请求处理时间会因为GC停顿增加50ms,服务的可用性最低可能为99.6%,无法满足可用性要求。

ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器,可用于大内存低延迟服务的内存管理和回收。

2 GC基础概念

  • 垃圾:不再使用的对象。在JVM中,指的是一个没有引用指向的对象,或者仅包含循环引用的多个对象。
  • GC:Garbage Collection,垃圾回收
  • STW:Stop The World,GC的停顿时间,此时所有用户线程停止活动,等待GC线程完成垃圾回收。
  • 用户线程:用于执行业务逻辑的线程。
  • GC线程:用于垃圾收集的线程。
  • 并发:在多核条件下,用户线程和GC线程同时执行。
  • 并行:在多核条件下,多个GC线程同时执行。

3 ZGC简介

3.1 特点

  1. 单代内存管理
  2. 内存分页管理:使用小、中、大三种粒度。

3.2 优点

  1. 停顿时间不会随着堆内存的增大而增长:ZGC几乎所有暂停都只依赖于根集合大小,停顿时间不会随着堆内存的大小而增加。
  2. 单次停顿时间控制在10ms之内。在实际项目中通常停顿时间小于15ms。

3.3 缺点

  1. 吞吐量低,不适合吞吐量优先的程序。
  2. CPU占用率偏高:在实际项目中,服务使用zgc后,机器CPU负载会增加10%
  3. 对机器CPU负载敏感:在实际项目中,发现机器的CPU负载达到到60%以上,ZGC的内存回收速度会下降,导致GC周期增长。
  4. 存在浮动垃圾。
  5. 当内存回收速度低于分配速度,可用内存不足时,会退化成阻塞回收,所有用户线程暂停,直到GC完成。

4 低延迟的原因

ZGC在标记、转移和重定位阶段几乎都是并发的,这是ZGC实现停顿时间小于10ms的最关键原因。
ZGC 只有 3 个需要 STW 的阶段,其中初始标记和初始转移只需要扫描所有根集合,STW 时间根集合 的数量成正比,不会耗费太多时间。再标记过程主要处理并发标记引用地址发生变化的对象,这些对象数量比较少,耗时非常短。

  1. 初始标记:从根集合出发,标记根集合所有引用的对象。需要STW。
  2. 并发标记:以第1步找到的标记对象为起点,进行遍历和标记。
  3. 再标记:完成对并发标记过程中尚未标记的对象的标记。需要STW。
  4. 并发转移准备:确定需要清理的页面
  5. 初始转移:将需要清理的页面中,根集合引用对象移动到新页面。需要STW。
  6. 并发转移:将需要清理的页面中,存活对象移动到新页面。

5 关键技术

5.1 内存多重映射

内存多重映射,就是把不同的虚拟内存地址映射到同一个物理内存地址上。
ZGC 使用了内存多重映射,把同一块儿物理内存映射为 Marked0、Marked1 和 Remapped 三个虚拟内存。
Marked0、Marked1 和 Remapped 这三个虚拟内存作为 ZGC 的三个视图空间,在同一个时间点内只能有一个有效。ZGC 就是通过这三个视图空间的切换,来完成并发的垃圾回收。

5.2 着色指针

着色指针是一种将信息存储在指针中的技术。
ZGC 出现之前, GC 信息保存在对象头的 Mark Word 中。
ZGC将对象存活信息存储在42~45位中。

JVM 可以从指针上直接看到对象的三色标记状态(Marked0、Marked1)、是否进入了重分配集(Remapped)、是否需要通过 finalize 方法来访问到(Finalizable)。无需进行对象访问就可以获得 GC 信息,这大大提高了 GC 效率。

5.3 内存布局

为了细粒度地控制内存的分配,ZGC将内存分为小、中、大三种页面:

  1. 小页面(Small Page) : 容量固定为2MB, 用于放置小于256KB的小对象。
  2. 中页面(Medium Page) : 容量固定为32MB, 用于放置大于等于256KB但小于4MB的对象。
  3. 大页面(Large Page) : 容量不固定, 可以动态变化, 但必须为2MB的整数倍, 用于放置4MB或以上的大对象。 每个大型Region中只会存放一个大对象。

5.4 读屏障

读屏障是JVM向应用代码插入一小段代码的技术。
ZGC中读屏障的代码作用:在对象标记和转移过程中,用于确定对象的引用地址是否满足条件,并作出相应动作。
当应用线程从堆中读取对象引用时,就会执行这段代码。

Object o = obj.FieldA
<Load barrier>
Object p = o //不是从堆中读取引用
o.dosomething() //不是从堆中读取引用
int i =  obj.FieldB //不是引用类型

6 异常情况说明

6.1 GC异常情况

如果内存分配速度大于回收速度,在GC执行过程中,服务可能无可用内存,这时ZGC会进入阻塞回收模式。所有的用户线程会停止,直到GC执行完毕。
这种情况,在ZGC中称之为“Allocation Stall”

6.2 内存分配异常

"Page Cache Flush"问题影响分配速度:

  • 如果各种大小对象分配速度不稳定,比如中等大小的对象突然变多,中页面不足,那么就需要把小页面或者大页面转换成中页面,转换比较耗时,从而影响内存分配速度。
  • GC日志中的关键字为“Page Cache Flush”。

7 GC日志

7.1 GC触发时机

ZGC有多种GC触发机制,具体如下:

  1. 阻塞内存分配请求触发:当垃圾来不及回收,可用内存不足时触发。应当避免出现这种触发方式。日志中关键字是“Allocation Stall”。
  2. 基于分配速率的自适应算法:最主要的GC触发方式,其算法原理可简单描述为”ZGC根据近期的对象分配速率以及GC时间,计算出当内存占用达到什么阈值时触发下一次GC”。日志中关键字是“Allocation Rate”。
  3. 基于固定时间间隔:日志中关键字是“Timer”。
  4. 主动触发规则:类似于固定间隔规则,但时间间隔不固定,是ZGC自行算出来的时机。日志中关键字是“Proactive”。
  5. 预热规则:服务刚启动时出现,一般不需要关注。日志中关键字是“Warmup”。
  6. 外部触发:代码中显式调用System.gc()触发。 日志中关键字是“System.gc()”。
  7. 元数据分配触发:元数据区不足时导致,一般不需要关注。 日志中关键字是“Metadata GC Threshold”。

7.2 GC日志解读

GC日志中每一行都注明了GC过程中的信息,关键信息如下:

  • Start:开始GC,并标明的GC触发的原因。
  • Phase-Pause Mark Start:初始标记,会STW。
  • Phase-Pause Mark End:再次标记,会STW。
  • Phase-Pause Relocate Start:初始转移,会STW。

GC信息统计:定时的打印垃圾收集信息,标注了10秒内、10分钟内、10个小时内的统计信息。
如下图所示。

8 常用GC参数

以jdk11为例,如下是一个程序的GC参数:

java -Xms256m -Xmx256m -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:ConcGCThreads=2 -XX:ParallelGCThreads=3 -XX:ZCollectionInterval=60 -XX:ZAllocationSpikeTolerance=4 -XX:+ZProactive -Xlog:gc*:file=gc.log:time,tid,tags -cp work.jar Main

内存大小:

  • -Xms:堆的最大内存
  • -Xmx:堆最小内存

启用zgc:

  • -XX:+UnlockExperimentalVMOptions -XX:+UseZGC:启用ZGC的配置。

GC线程数设置:

  • -XX:ConcGCThreads:并发回收垃圾的线程。示例中是2,默认是总核数的12.5%。调大后GC变快,但会占用程序运行时的CPU资源,吞吐会受到影响。
  • -XX:ParallelGCThreads:STW阶段使用线程数,示例中是3,默认是总核数的60%。

触发方式设置:

  • -XX:ZCollectionInterval:ZGC发生的最小时间间隔,单位秒。示例中是60秒。
  • -XX:ZAllocationSpikeTolerance:ZGC触发自适应算法的修正系数,默认2,数值越大,越早的触发ZGC。示例中是4。
  • ZProactive:是否启用主动回收,默认开启。 -XX:-ZProactive代表关闭

9 调优案例

9.1 服务改为ZGC后,STW时间增加

背景:

  1. 服务将GC从G1改为ZGC后,晚高峰STW停顿时间上升。

分析:

  1. 观察GC日志,主要是初始标记和初始转移耗时高导致。
  2. 该服务内有大量永久生存的对象,导致从根节点扫描和转移耗时较久。

解决方法:

  1. 使用堆外内存的方式将对象从堆中移出。

9.2 流量突增,服务出现阻塞分配的情况

背景:

  1. 服务将GC从G1改为ZGC,在应对突发流量时,停顿时间过长,GC日志出现大量“Allocation Stall”。

分析:

  1. 出现突发流量,内存分配速度高于回收速度。
  2. 在出现”Allocation Stall“之前,两次GC的间隔时间基本为0.
  3. 尝试增大GC的线程数,发现无明显效果。
  4. 将内存增大30%,“Allocation Stall”消失,两次GC间隔时间变长。

解决方法:

  1. 增大内存,增大两次GC之间的间隔,保证绝大部分对象在GC开始时已处于可回收状态。

9.3 服务内存分配速率不均,导致服务性能出现毛刺

背景:

  1. 服务将GC从G1改为ZGC后,STW耗时下降,但是观察可用性监控,出现可用性下降的毛刺。

分析:

  1. 查看GC日志,出现较多“Page Cache Flush”。
  2. 比较GC日志和服务日志,定位到问题代码。
  3. 该代码定时获取并解析来自Web API的json字符串。在解析前,会申请一个2MB的空间用于存放字符串。

解决方案:

  1. 定位到内存分配不均的代码,将原有解析代码改为流式解析。

10 总结

ZGC垃圾回收过程几乎全部是并发,实际STW停顿时间极短,不到10ms。
对于低延迟高可用的Java服务,ZGC可用满足其极低的暂停需求。

11 参考资料

  • 新一代垃圾回收器ZGC的探索与实践
  • ZGC官网
  • 《新一代垃圾回收器ZGC设计与实现》
  • ZGC原理是什么,它为什么能做到低延迟

低延迟垃圾回收器ZGC相关推荐

  1. 新一代垃圾回收器ZGC的探索与实践

    很多低延迟高可用Java服务的系统可用性经常受GC停顿的困扰,作为新一代的低延迟垃圾回收器,ZGC在大内存低延迟服务的内存管理和回收方面,有着非常不错的表现. 本文从GC之痛.ZGC原理.ZGC调优实 ...

  2. c++ 多线程 垃圾回收器_新一代垃圾回收器ZGC的探索与实践

    很多低延迟高可用Java服务的系统可用性经常受GC停顿的困扰,作为新一代的低延迟垃圾回收器,ZGC在大内存低延迟服务的内存管理和回收方面,有着非常不错的表现. 本文从GC之痛.ZGC原理.ZGC调优实 ...

  3. 【美团技术团队搬运】新一代垃圾回收器ZGC的探索与实践

    新一代垃圾回收器ZGC的探索与实践 2020年08月06日 作者: 王东 王伟 文章链接 12996字 26分钟阅读 ZGC(The Z Garbage Collector)是JDK 11中推出的一款 ...

  4. 垃圾回收器ZGC应用分析总结

    目录 一.基本概述 二.基本关键技术知识总结 (一)三色标记法(着色指针) (二)读屏障 (三)多图映射 (四)简单场景说明ZGC并发 三.基本回收原理介绍 四.ZGC调优案例实践 (一)调优基础知识 ...

  5. JVM 垃圾回收器 ZGC

    JVM 垃圾回收器 ZGC简介 学习ZGC,主要通过学习ZGC设计与实现书籍,并以博客的形式记录学习内容 ZGC在JDK11引入,目前被标记为实验性质.ZGC的出现是为了解决G1的不足. G1不足之处 ...

  6. 【java】java 新一代垃圾回收器ZGC的探索与实践

    1.概述 转载:新一代垃圾回收器ZGC的探索与实践

  7. Jdk11,Jdk12的低延迟垃圾收集器ZGC

    https://wiki.openjdk.java.net/display/zgc/Main Z垃圾收集器,也称为ZGC,是一种可扩展的低延迟垃圾收集器,旨在实现以下目标: 暂停时间不超过10毫秒 暂 ...

  8. 新一代垃圾回收器—ZGC

    目录 1.什么是ZGC 2.ZGC设计目的 3.ZGC现状 4.ZGC的核心技术 4.1 多重映射 4.2 染色指针 4.3 读屏障 1.什么是ZGC ZGC(The Z Garbage Collec ...

  9. 新一代垃圾回收器ZGC

    JVM垃圾收集器的发展过程从某种角度来说可以看作人类在不断追求STW尽量短暂的过程,而这个过程中涌现了很多优秀的垃圾收集器.从开始的单线程 Serial GC到 CMS 再到 G1,直到 ZGC的出现 ...

最新文章

  1. [原创]结构在Loadrunner中的应用
  2. 案例:验证用户名是否可用
  3. 提升的控件 paint()事件被遮挡_设计锦囊 | 提升产品易用性案例分享
  4. c语言千位数字,C语言怎样提取一个数的十位个位百位千位?
  5. 基于 Azure 的认知服务将文本合成语音
  6. MongoDB via Dotnet Core数据映射详解
  7. android根据拍摄url获取格式,Android如何通过URI获取文件路径示例代码
  8. 清华大学-美团数字生活联合研究院成立
  9. .Net Core+mySqlSugar的一些稍复杂操作
  10. java改变表格标题,DIV 常用标题表格
  11. Java语言程序设计(基础篇) 第十一章 继承和多态
  12. java根据数据库自动生成代码
  13. 计算机室 多媒体教室制度,多媒体教室计算机室规章制度.doc
  14. linux 摄像头yuv,camera YUV格式
  15. gitlab发邮件收不到
  16. 三维实时云渲染平台解决方案
  17. 【JavaScript】JS高级-面向对象编程
  18. 华为OD机试真题 Java 实现【服务中心选址】【2023 Q1 | 200分】
  19. python爬取美女_Python爬取高颜值美女(爬虫+人脸检测+颜值检测)附学习教程
  20. 基于51单片机的扫地小车,扫地机器人设计 主要功能有寻迹避障,来回清扫功能,往返清扫功能

热门文章

  1. Windows操作快捷键大全
  2. Blynk + haodaMIDI,一个人就是一支乐队!
  3. 关于java开发银行业务_一文教你使用java开发银行柜员业务绩效考核系统
  4. 搭载AI的真·物理自瞄外挂,宣称不会被发现,动视:已连锅端
  5. 在FastAdmin中引入Jquery插件
  6. BeetlSQL简单使用
  7. 分享好文也能赚钱是真的! KOKO币奖励你线上阅读、分享!
  8. 非常强大的java时间处理工具类!
  9. 聚观早报|马斯克有意出价45亿英镑收购曼联;威马员工将停薪留职
  10. Android中 备份短信 还原短信