(12)ZGC美团

2020-11-21  本文已影响0人  hedgehog1112

JDK 11中低延迟垃圾回收器,设计目标:

    停顿时间 < 10ms,不随堆、活跃对象的大小而增加

    支持8MB~4TB的堆(未来支持16TB)

概要:GC之痛(CMS和G1停顿时间瓶颈) 、ZGC原理(更短)、ZGC调优实践、升级ZGC效果

一、GC之痛

可用性99.99%未达到:CMS单次Young GC 40ms,一分钟10次,接口响应时间30ms。( 40ms + 30ms ) * 10次 / 60000ms = 1.12%

四个STW:两次标记、复制(耗时长)、清理。  ps:G1和CMS的Young GC,标记-复制全过程STW

二、ZGC原理

标记、转移和重定位阶段几乎都并发

1、三个STW:1\2)初始标记转移(短):,只扫描所有GC Roots,处理时间和GC Roots数量成正比 3)再标记(短):最多1ms,超过1ms再次进入并发标记

    STW只依赖GC Roots大小,和堆或者活跃对象没关系

2、ZGC关键技术

着色指针读屏障,实现并发转移,转移过程准确访问对象。

原理:1)old:转移时,应用线程不停访问对象。可能旧地址造成错误。2)ZGC访问触发“读屏障”,发现对象移动(着色指针判断)“读屏障”更到对象新地址

(1)着色指针

将信息存在指针中,ZGC仅支持64位系统,把64位虚拟地址空间划分为多个子空间。ZGC仅用0~41位,42~45存元数据(存活信息,和传统gc完全不同,第47~63位固定为0

创建对象时:1)空间申请虚拟地址(不映射到真正物理地址),2)ZGC同时为该对象在M0、M1和Remapped分别申请虚拟地址(三个虚拟对应同一物理地址,同一时间只一个有效),3)设置三个,因“空间换时间”,降低GC停顿时间

[0~4TB) 对应Java堆,[4TB ~ 8TB) 称为M0地址空间,[8TB ~ 12TB) 称为M1地址空间,[12TB ~ 16TB) 预留未使用,[16TB ~ 20TB) 称为Remapped空间。

(2)读屏障

JVM应用代码插入一小段代码,应用线程读对象引用执行。注意:仅“从堆中读取对象引用”才触发

作用:对象标记和转移时,确定对象引用地址是否满足条件,作出相应动作。

读屏障示例

(3)ZGC并发处理演示(gc中地址视图切换过程)

    1) 初始化地址视图设置为Remapped。程序运行,内存中分配对象,满足条件后gc,标记

    2) 并发标记阶段:第一次标记为M0,如对象GC标记或者应用线程访问过,将对象Remapped调为M0(说明对象活跃)

  3) 并发转移阶段:再次被设为Remapped。如GC转移或应用线程访问过,将对象从M0调整为Remapped。ps:区别第二次标记,调为M1

“着色指针和读屏障”用在并发转移、标记阶段,1)传统回收器:已标记进行一次内存访问,并将对象存活信息放在对象头;2)ZGC:只设置指针地址第42~45位即可,因为是寄存器访问,比访问内存更快

三、ZGC调优实践

1、重要参数

-Xms -Xmx:堆最大/小内存,都设为10G,堆内存保持不变

-XX:ReservedCodeCacheSize -XX:InitialCodeCacheSize: CodeCache的大小, JIT编译的代码都放在CodeCache中,一般服务64m或128m足够。

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC:启用ZGC

-XX:ConcGCThreads:并发回收垃圾的线程。默认总核数12.5%,8核CPU是1。调大GC变快,但占用CPU资源,影响吞吐

-XX:ParallelGCThreads:STW阶段使用线程数,默认总核数60%。

-XX:ZCollectionInterval:ZGC最小时间间隔,秒。

-XX:ZAllocationSpikeTolerance:ZGC触发自适应算法的修正系数,默认2,数值越大,越早的触发ZGC。

-XX:+UnlockDiagnosticVMOptions -XX:-ZProactive:是否启用主动回收,默认开启,这里表示关闭。

-Xlog:设置GC日志中的内容、格式、位置以及每个日志的大小。

2、理解ZGC触发时机

调优第一目标:保证GC完成之前,新对象不会占满堆,导致线程停顿,秒级。触发机制:

    1)阻塞内存分配请求触发:来不及回收,将堆占满时,阻塞。日志关键字“Allocation Stall”。

    2)基于分配速率的自适应算法(最主要):根据近期对象分配速率GC时间,计算内存占用阈值,触发GC”。ZAllocationSpikeTolerance控制阈值大小,默认2,越大越早触发。“Allocation Rate”。

    3)基于固定时间间隔:ZCollectionInterval控制,适合增流量场景,自适应算法触发可能会过晚,阻塞。“Timer”。

    4)主动触发规则:类似3,间隔不固定,ZGC自行算,通过-ZProactive关闭,以免GC频繁,影响服务可用性。“Proactive”。

    5)预热规则:服务刚启动,不需关注“Warmup”。

    6)外部触发:代码中显式调System.gc()。System.gc()”。

    7)元数据分配触发:元数据区不足,不需关注。Metadata GC Threshold

3、理解ZGC日志(完整GC过程)

该日志过滤了进入安全点的信息。正常情况,在一次GC过程中还穿插着进入安全点的操作。

Start:开始GC,GC触发原因。上图是自适应算法。

Phase-Pause Mark Start:初始标记,会STW。

Phase-Pause Mark End:再次标记,会STW。

Phase-Pause Relocate Start:初始转移,会STW。

Heap信息:记录GC中Mark、Relocate前后堆大小变化。High和Low记录最大/小值,如100%,GC一定内存分配不足,更早更快触发GC时机

GC信息统计:定时打印垃圾收集信息,10秒内、10分钟内、10个小时内,启动到现在所有统计信息。排查异常点

4、理解ZGC停顿原因(6种)

    1)初始标记:日志中Pause Mark Start。

    2)再标记:日志中Pause Mark End。

    3)初始转移:日志中Pause Relocate Start。

    4)内存分配阻塞:内存不足阻塞"Allocation Stall"。

    4)安全点所有线程进入安全点才能GC,定期进入判断是否要GC。先等后,直到所有挂起

    6)dump线程、内存:比如jstack、jmap命令

5、案例一:秒杀流量突增,出现性能毛刺

日志:大量“Allocation Stall”,多出现在“自适应算法,内存分配阻塞

解决方法:1)固定间隔触发-XX:ZCollectionInterval。调为5秒,更短

                  2)更早触发GC,增大-XX:ZAllocationSpikeTolerance,预测内存分配速率,默认值2设置5,值越大越早触发

6、案例二:压测时,流量逐渐增大一定程度后,出现性能毛刺

日志信息:平均1秒GC/次,两次几乎没间隔,内存分配阻塞

分析:触发及时,但内存标记和回收慢

解决方法:加快并发标记和回收速度,增大-XX:ConcGCThreads默认值核数1/8,8核是1。影响系统吞吐,如GC间隔时间 > GC周期,不建议调整

GC Roots 数量大,单次GC停顿时间长

四、升级ZGC效果

1、延迟降低

低延迟(TP999 < 200ms)场景中收益较大:

    TP999(99.9%请求都能被响应的最小耗时):下降12~142ms,下降幅度18%~74%。

    TP99:下降5~28ms,下降幅度10%~47%。

超低延迟(TP999 < 20ms)和高延迟(TP999 > 200ms)收益不大,瓶颈不是GC,是外部依赖的性能。

2、吞吐下降

不适合吞吐量优先场景。CMS升级ZGC,吞吐量明显降低。1)ZGC单代,每次处理对象更多,耗CPU2)读屏障,耗费额外计算资源

总结:性能优秀:全部并发;STW极短<10ms;得益于着色指针和读屏障

https://zhuanlan.zhihu.com/p/170572432

上一篇下一篇

猜你喜欢

热点阅读