JVM性能及GC调优

当线上Java程序性能逐渐下降,通过一系列优化手段也提升有限时,通常需要调整垃圾回收器来进一步提高性能,称为GC优化。从性能来说,GC调优主要关注以下三个评分指标:内存占用、延迟、吞吐量。影响GC性能的参数众多,且参数调整又依赖于应用各自的特点,这些因素很大程度上增加了GC优化的难度,本文介绍了线上GC调优需要学习的一些知识点。

1. Java运行参数设置及优化

Java运行时数据区如图所示:

image

我们常说的GC大部分指的是Java Heap的一系列操作。本文的所有操作,是基于JDK1.8版本的。

1.1 堆参数

  • -Xms,堆的初始值,等价于-XX:InitialHeapSize,比如-Xms512m表示初始堆大小为512Mb,-Xms4g表示初始堆大小4g;
  • -Xmx,堆的最大值,等价于 -XX:MaxHeapSize,-Xmx8g表示最大堆为8g;
  • -Xmn,新生代及年轻代大小,则老年代的大小=Xmx-Xmn;
  • -XX:SurvivorRatio,年轻代中Eden区所占比例,默认是8,也就是Eden默认占80%;

最好将 -Xms 和 -Xmx 的值设置成一样的值,这样做是为了防止随着堆空间使用量增加,会动态的调整堆空间大小,有一定的性能损耗,不如开始就设置成相同的值,来规避性能损失。

1.2 栈参数

  • -Xss,栈空间大小,栈是线程独占的,所以是一个线程使用栈空间的大小,默认值是1M;

Each thread in a Java application has its own stack. The stack is used to hold return addresses, function/method call arguments, etc. So if a thread tends to process large structures via recursive algorithms, it may need a large stack for all those return addresses and such. With the Sun JVM, you can set that size via that parameter.

1.3 Metaspace参数

  • -XX:MetaspaceSize,Metaspace空间初始大小,默认是20.79M,这个初始大小是触发首次Metaspace Full GC的阈值,配置案例如 -XX:MetaspaceSize=256M;
  • -XX:MaxMetaspaceSize,Metaspace 最大值,默认不限制大小;
  • -XX:MinMetaspaceFreeRatio,最小空闲比,当Metaspace发生GC后,会计算Metaspace 的空闲比,如果空闲比(空闲空间/当前Metaspace大小)小于此值,就会触发Metaspace扩容。默认值是40 ,也就是40%,配置案例如 -XX:MinMetaspaceFreeRatio=40;
  • -XX:MaxMetaspaceFreeRatio,最大空闲比,当Metaspace发生GC后,会计算Metaspace的空闲比,如果空闲比(空闲空间/当前Metaspace大小)大于此值,就会触发Metaspace释放空间。默认值是70 ,也就是70%,配置案例如 -XX:MaxMetaspaceFreeRatio=70;

1.4 JVM运行配置参数

  • -verbose:gc或-XX:+PrintGC,简单输出GC日志;
  • -Xloggc:/logs/gc.log,GC日志输出位置;
  • -XX:+PrintGCDetails,输出GC详细日志;
  • -XX:+PrintGCDateStamps,输出GC的时间戳,以日期的形式,如2020-03-11T17:22:48.180+0800;
  • -XX:+PrintGCTimeStamps,输出GC的时间戳;
  • -XX:+PrintHeapAtGC,在进行GC的前后打印出堆的信息;
  • -XX:+PrintGCApplicationStoppedTime,打印GC导致的Stop The World时间;
  • -XX:+PrintClassHistogramBeforeFullGCXX:+PrintClassHistogramAfterFullGC,GC前后的类加载情况;
  • -XX:+HeapDumpOnOutOfMemoryError,内存溢出时自动导出,内存很大的时候,可能会导不出来,使用XX:HeapDumpPath=dir导出内存映像文件;

1.5 设置垃圾回收器

JDK8可使用的垃圾收集器有7种,当然有的只适用于年轻代,有的只适用于老年代,JDK8中最新的垃圾收集器是G1,可以用于年轻代和老年代,到了JDK11,还出了ZGC。

  • -XX:+UseSerialGC,虚拟机Client模式下的默认值,使用Serial(新生代)+ Serial Old(老年代)收集器;
  • -XX:+UseParNewGC,使用ParNew + Serial Old,JDK9后不再支持;
  • -XX:+UseConcMarkSweepGC,使用ParNew + CMS + Serial Old组合收集器,Serial Old作为CMS出现“Concurrent Mode Failure”错误后的备选;
  • -XX:+UseParallelGC,使用Parallel Scavenge(新生代) + Serial Old(老年代)收集器,JDK9之前Server模式下的默认设置;
  • -XX:+UseParallelOldGC,使用Parallel Scavenge(新生代) + Parallel Old(老年代)收集器;
  • -XX:+UseG1GC,使用G1垃圾收集器,JDK9之后的Server模式默认值;

1.6 远程JMX设置

当我们需要查看JVM运行状态,第一种是登陆JVM服务器,使用jmap、jstack、jstat等工具查看;第二种是开启JMX远程功能,使用jConsole、VisualVM等工具进行监控。开启的参数如下:

1
2
3
4
5
6
7
8
-Dcom.sun.management.jmxremote
#指定jvm所在服务器ip或域名
-Djava.rmi.server.hostname=192.168.1.1
#指定端口
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.rmi.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

2. GC日志分析

2.1 Young GC日志

一段典型的Young GC(Minor GC)的日志如下:

1
2020-03-11T17:43:28.034+0800: 20595.056: [GC (Allocation Failure) [PSYoungGen: 4185120K->4923K(4188160K)] 4547662K->368154K(8382464K), 0.0071567 secs] [Times: user=0.13 sys=0.00, real=0.01 secs]

Young GC说明图示:

image

2.2 Full GC日志

Full GC日志:

1
2018-01-10T16:53:43.811+0800: 980.825: [Full GC (Metadata GC Threshold) [PSYoungGen: 21613K->0K(231424K)] [ParOldGen: 390439K->400478K(761856K)] 412053K->400478K(993280K), [Metaspace: 314108K->313262K(1458176K)], 1.2320834 secs] [Times: user=7.86 sys=0.06, real=1.23 secs]

Full GC说明图示:

image

2.3 GC日志分析工具

GChisto,是一款专业分析gc日志的工具,可以通过gc日志来分析:Minor GC、full gc的时间、频率等等,通过列表、报表、图表等不同的形式来反应gc的情况:

image

GC Easy是一款在线的GC日志分析工具,将gc的log上传后,直接查看结果:

image
image

3. 常用JDK工具

3.1 可视化工具类

工具 说明
jconsole 用于监控Java虚拟机的使用JMX规范的图形工具。它可以监控本地和远程JVM。它还可以监控和管理应用程序。
jvisualvm 提供内存和CPU分析,堆转储分析,内存泄漏检测等监控。

3.2 监控类

jstat是查看JVM统计信息的工具,jstat的命令格式为:

1
2
3
4
5
6
jstat [options] pid [interval] [count]
options,一般使用 -gcutil 或 -gc 查看gc情况
pid,当前运行的Java进程号
interval,间隔时间,单位为秒或者毫秒
count,打印次数,如果缺省则打印无数次

options参数如下:

1
2
3
4
5
6
7
8
9
10
11
-gc:统计 jdk gc时 heap信息,以使用空间字节数表示
-gcutil:统计 gc时 heap情况,以使用空间的百分比表示
-class:统计 class loader行为信息
-compile:统计编译行为信息
-gccapacity:统计不同 generations(新生代,老年代,持久代)的容量使用的最大最小值,例如使用到的最大值、最小值、当前使用值等等
-gccause:统计引起 gc的事件
-gcnew:统计 gc时,新生代的情况
-gcnewcapacity:统计 gc时,新生代 heap容量
-gcold:统计 gc时,老年代的情况
-gcoldcapacity:统计 gc时,老年代 heap容量
-gcpermcapacity:统计 gc时, permanent区 heap容量

以jstat -gc 60067 10000 5命令为例,运行结果为:

image

每一列的含义为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
S0C: 第1个Survivor空间的容量 Current survivor space 0 capacity (kB).
S1C: 第2个Survivor空间的容量(kB).
S0U: 第1个Survivor空间中已经使用的容量 Survivor space 0 utilization (kB).
S1U: 第2个Survivor空间中已经使用的容量(kB).
EC: Eden空间的容量(kB).
EU: Eden空间中已经使用的容量(kB).
OC: 老年代Old空间的容量(kB).
OU: 老年代Old空间已经使用的容量(kB).
MC: 元空间Metaspace的容量(kB).
MU: 元空间Metaspace已经使用的容量(kB).
CCSC: 压缩类空间的容量 Compressed class space capacity (kB).
CCSU: 压缩类空间已经使用的容量(kB).
YGC: Young GC发生的次数.
YGCT: Young GC花费的时间.
FGC: Full GC发生的次数.
FGCT: Full GC花费的时间.
GCT: 所有的GC花费的总时间.

jps是进程状态工具(JVM Process Status Tool),在目标系统上列出HotSpot Java虚拟机进程的描述信息。

3.3 故障分析类

工具 说明
jinfo Java的配置信息工具(Java Configuration Information),用于打印指定Java进程、核心文件或远程调试服务器的配置信息。
jhat Java堆分析工具(Java Heap Analysis Tool),用于分析Java堆内存中的对象信息。
jmap Java内存映射工具(Java Memory Map),主要用于打印指定Java进程、核心文件或远程调试服务器的共享对象内存映射或堆内存细节,打印内存dump文件等。
jstack Java的堆栈跟踪工具,主要用于打印指定Java进程、核心文件或远程调试服务器的Java线程的堆栈跟踪信息。

3.4 其他

Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。支持在线排查程序问题,无需重启,可动态跟踪Java代码,可实时监控JVM状态。支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的Tab自动补全功能,进一步方便进行问题的定位和诊断。

4. 参考文档

以上内容就是关于JVM性能及GC调优的全部内容了,谢谢你阅读到了这里!

Author:zhaoyh