Post

JVM G1性能调优

G1简介和常用配置可参考:JVM G1垃圾回收器简介与常用配置

G1调优概要

G1整体上保持默认配置即可,最多配置一下 pause-time goal或堆内存大小(-Xmx-Xms)。

不像其他的垃圾回收器,G1默认已经在最大吞吐和低时延之间做了平衡。但是,G1在堆中的增量式空间回收和pause-time控制机制给应用线程 和回收效率上都带来了负担。

如果需要大吞吐(非在线业务,如后台任务),可以适当调大期望最大停顿时间(-XX:MaxGCPauseMillis) 或者调大堆空间。

如果需要低时延(在线业务),可以调小最大停顿时间。

不要配置-Xmn,-XX:NewRatio等参数,这些会限制年轻代空间大小,而年轻代是G1实现预期停顿时间的主要方式,将年轻代大小设置为一个固定值会导致G1的停顿时间控制失效。

G1性能调优

G1在大多数场景不需要要调优,针对需要调优的场景,本章给出一些性能诊断和调优指南。

为了定位性能瓶颈,可以配置参数-Xlog:gc*=debug

低时延调优

gc+cpu=info日志会打印pause-time的耗时详情,如User=0.19s Sys=0.00s Real=0.01s

其中,User表示进程执行用户态代码(核心之外)消耗的时间,Sys表示进程在内核态消耗的时间,Real表示程序从开始到结束所用的时间。 这里User + Sys 时间的和比 Real 时间要大,这主要是因为日志时间是从 JVM 中获得的,而这个 JVM 在多核的处理器上被配置了多个 GC 线程, 由于多个线程并行地执行 GC,因此整个 GC 工作被这些线程共享,最终导致 Real < User + Sys,如果是单线程的垃圾回收器,如Serial垃圾回收器,则 Real = User + Sys

如果Sys时间过大,可能的原因有:

VM向操作系统申请内存时导致的delay,可以将 -Xms 和 -Xmx 值设为一致, 避免内存的频繁分配。通过JVM的参数-Xmx和-Xms可以设置JVM的堆大小, 但是此时操作系统分配的只是虚拟内存,只有JVM真正要使用该内存时,才会被分配物理内存。 使用-XX:+AlwaysPreTouch参数在服务启动时即分配物理内存给VM

特别是在Linux中, Transparent Huge Pages (THP) 特性会随机暂停进程,不仅仅在pause-time时, 可以参考对应的操作系统去关闭THP特性

写日志可能会线程暂停,日志线程会占用写磁盘的带宽。

如果Real » User + Sys 则表示VM没有获得足够的CPU时间片,机器配置低了。


Young-Only Collection 耗时过长。一般来讲,年轻代垃圾回收耗时和年轻代空间大小成正比,主要原因是G1通过复制-整理方式 来聚合GC后的对象(即复制到Survivor区)。可以通过减小 -XX:G1NewSizePercent-XX:G1MaxNewSizePercent 来限制年轻代占整个已使用堆内存的最小和最大百分比,以此来减小单次GC耗时。


Mixed Collection 耗时过长。Mixed Collection包含年轻代和老年代的Regions,可以配置gc+ergo+cset=trace观察 年轻代和老年代在Mixed Collection过程中的耗时占用。如果是老年代GC耗时过长,可以做如下调整:

  • 增大 -XX:G1MixedGCCountTarget=8:最多8次混合垃圾回收, 设置标记周期完成后, 对存活数据上限为 G1MixedGCLIveThresholdPercent 的旧区域执行混合垃圾回收的目标次数
  • 调整 XX:G1MixedGCLiveThresholdPercent=70:默认值 : 65%, MixGC时, 年老代Region中存活对象百分比, 只有在此阈值下的Region才会被选入回收列表CSet (需开启-XX: +UnlockExperimentalVMOptions )
  • 增大 -XX:G1HeapWastePercent=10:默认值 : 10% , 堆浪费百分比, 当G1发现可被回收的空间小于10%时, 就不会再进行混合收集, 也就是会结束当前的混合收集周期

同时,可以调整-XX:GCPauseIntervalMillis参数来控制两次GC的时间间隔


G1使用Remember Set(RS)记录跨Region的对象引用,如果RS扫描和更新时间长,可以适当减小Region大小,通过配置-XX:G1HeapRegionSize=4M实现。 同时,G1会并发来更新RS,

VM有优化尝试减少并发RS更新来批量更新RS,如果批量更新的批次在GC前创建,则会导致RS pause-time变大,可以通过-XX:-ReduceInitialCardMarks关闭该优化。

RS扫描时间高也可能是由于大量的RS压缩(remembered set coarsening)导致,coarsening可以减小RS空间,但会增加RS变更和获取的耗时。 该压缩默认开启,可以配置-XX:G1SummarizeRSetStatsPeriodgc+remset=trace查看是否发生了coarsening,增大-XX:G1RSetRegionEntries可以降低coarsening的数量。

同时,生产环境应避免开启RS详细日志。

大吞吐调优

一些后台任务场景更加重视吞吐而不是时延,可以通过一下配置优化吞吐性能:

  • 增大-XX:MaxGCPauseMillis: 增大GC最大耗时
  • 增大-XX:G1MaxNewSizePercent: 增大年轻代占已使用堆空间的百分比
  • 减小并行工作:增大-XX:G1RSetUpdatingPauseTimePercent,关闭并行更新RS可配置 -XX:-G1UseAdaptiveConcRefinement -XX:G1ConcRefinementGreenZone=2G -XX:G1ConcRefinementThreads=0
  • 开启大内存也使用:-XX:+UseLargePages
  • -Xms-Xmx值设为一样,并配置-XX:+AlwaysPreTouch

堆空间调优

-XX:GCTimeRatio表示GC外的耗时和GC耗时的比例,假设-XX:GCTimeRatio=19 ,则垃圾收集时间为1/(1+19),默认值为99,即1%时间用于垃圾收集

This post is licensed under CC BY 4.0 by the author.