1.工欲善其事:JDK提供的工具

  • Java试题

JVM调优的常见命令行工具描述正确的有:

A.jstat可以实时显示本地或远程JVM进程中类加载、内存、垃圾收集、JIT编译等数据

B.jmap用于查询当前运行的JVM属性和参数的值

C.jstack用于生成当前JVM的所有线程快照

D.jps命令用于查询正在运行的JVM进程

  • 试题解读

这道题考察JDK自带命令行工具的理解,这些工具用于性能调优、疑难问题定位。

在生产环境定位时,要求定位/调优工具自身具备极高的性能,否则会对本来就有问题的生产环境产生干扰。

JDK自带的命令行工具以其的高性能、实用性,成为实战中必须掌握的工具,是Java程序猿的调优武器库。

image-20210521073310456

2.总览

2.1.主流工具概览

目前,主流的Java性能监控与调优工具如下图所示:

image-20210521094038209

这些工具主要分为两个流派:“命令行流派"与"可视化流派

  • **命令行流派:**强调实用性。体积小、资源消耗小,适用于生产环境的问题定位与调优。
  • **可视化流派:**强调颜值即正义。可视化图表、一目了然,适用于开发环境上复现、定位问题。

image-20210521100601641

这两种流派各有适用的场景,辩证统一,所以还是那句话:“成年人不做选择,两样我都要”。

image-20210521104216938

1
2
说明1:本文仅讲解命令行流派的工具,笔者在后续文章中再探讨可视化流派工具。
说明2:不能简单地将Arthas看作工具,后续开专题探讨。

2.2.理解命令行工具的内在逻辑

对于这些命令行工具,有一种观点:没有必要记下来,用到的时候再查资料

因为这些命令行工具比较多,每个命令行也有很多参数,记下来是一件枯燥的事。

但,这个观点成立有一个前提:我们必须理解这些命令行工具的内在逻辑

  • **逻辑1:**这些命令行工具的关系是什么?
  • **逻辑2:**每个工具解决什么问题?
  • **逻辑3:**每个工具的参数集合分哪几类?

如果没有理解上述内在逻辑,需要用的时候再查资料也很难入手。

对于逻辑1:这些命令行工具的关系,笔者提供一种基于实战问题定位的场景的理解方式:

  • **STEP1.进程监控:**首先,找到生产环境中待定位的Java进程。
  • **STEP2.配置查询:**进一步,查看此Java进程的JVM配置。
  • **STEP3.JVM状态监控与初步分析:**再进一步,查看此Java进程的内存、线程、JIT的整体情况。
  • STEP4.内存监控与分析:如果STEP3发现了可能的内存问题,就深入分析内存。
  • **STEP5.线程监控与分析:**如果STEP3发现了可能的线程问题,就深入分析线程。

image-20210521111701312

2.3.进阶的方法

  • **命令行手册:**我们可以通过官方的命令行手册,深入学习它们。

image-20210521112755277

  • **工具源码:**通过这些命令行工具的源码,可以更深入地理解JVM状态监控的原理和API。
1
https://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/jdk.jcmd/share/classes/sun/tools

image-20210521112939223

3.进程监控-jps

3.1.解决什么问题

jps用来显示指定系统内所有正在运行的HotSpot虚拟机进程信息。

3.2.参数

  • jps的参数格式如下:
1
jps [options] [hostid]

我们可以通过几个实际的例子,快速理解[options]

  • **例子:**jps

  • **例子:**jps -q

  • **例子:**jps -lmv

image-20210521113905035

我们可以发现,无论怎样的options,一定会显示JVM identifiers,不同的options只是在JVM identifiers的基础上显示更多的额外信息。

了解到这个程度,实战中如有需要,可以再进一步查看命令行手册:

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jps.html#CHDCGECD

image-20210521114215905

4.配置查询-jinfo

4.1.解决什么问题

jinfo用来查看JVM虚拟机参数,同时可以修改部分JVM虚拟机参数。

4.2.参数

  • jinfo的参数格式如下:
1
jinfo [ option ] pid

我们可以通过几个实际的例子,快速理解[options]

  • **例子:**jinfo <pid>——查看此JVM进程所有的配置

image-20210521115021019

  • **例子:**jinfo -sysprops <pid>——等价于查看System.getProperties()

image-20210521115146579

  • **例子:**jinfo -flags <pid>——查看所有参数

image-20210521115222055

  • **例子:**jinfo -flags <参数名> <pid>——查看某个参数的值

image-20210521115244080

我们可以发现:

  • JVM进程的配置分为两类:系统环境变量JVM参数,jinfo的参数用来输出上述两类配置。
  • jinfo支持输出所有的配置值,也可以输出某一个指定参数的值。

了解到这个程度,实战中如有需要,可以再进一步查看命令行手册:

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jinfo.html#BCGEBFDD

image-20210521115706311

5.JVM状态监控与初步分析-jstat

5.1.解决什么问题

jstat可以输出JVM的各类状态(类加载、内存、GC、JIT)。

初步定位的时候,就是通过上述状态,找到诸如内存泄露、线程死锁等问题的蛛丝马迹。

5.2.参数

  • jstat的参数格式如下:
1
jstat [ generalOption | outputOptions vmid [ interval[s|ms] [ count ] ]
  • **例子:**jstat -class -t -h3 1000 5——采集类加载信息(每1000ms采样一次,采集5次,每3行打印1次表头)。

image-20210521132054749

  • **例子:**jstat -compiler -t -h3 1000 5——采集JIT信息(每1000ms采样一次,采集5次,每3行打印1次表头)。

image-20210521132151306

  • **例子:**jstat -printcompilation -t -h3 1000 5——采集已经被JIT编译过的方法(每1000ms采样一次,采集5次,每3行打印1次表头)。

image-20210521132611139

  • **例子:**jstat -gc -t -h3 1000 5——采集GC的情况(每1000ms采样一次,采集5次,每3行打印1次表头)。

  • **例子:**jstat -gccapacity -t -h3 1000 5

  • **例子:**jstat -gcutil -t -h3 1000 5

  • **例子:**jstat -gccause -t -h3 1000 5

image-20210521133656978

我们可以发现:

  • 采样相关的参数:-t(Timestamp列,单位s) 、-h(周期性的增加表头)、interval(采样周期,单位ms)、count(采集次数)
  • 类加载相关的参数:-class,显示ClassLoader信息,如:类加载数量、类卸载数量、总空间、类装载消耗的时间。
  • JIT相关的参数:-compiler(显示JIT编译过的方法、耗时)、-printcompilation(输出已经被JIT编译的方法)
  • **垃圾回收相关的参数:**gcXxx,例如:-gc:显示GC相关的堆信息。包括Eden、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。

在此展开看一下垃圾回收相关参数输出的Gc信息,我们可以发现无论哪个gcXxx参数,都会输出如下信息:

  • 新生代相关
1
2
3
4
5
6
S0C:第1个幸存者区的大小(字节)
S1C:第2个幸存者区的大小(字节)
S0U:第1个幸存者区的已使用大小(字节)
S1U:第2个幸存者区的已使用大小(字节)
EC:Eden区的大小(字节)
EU:Eden区的已使用大小(字节)
  • 老年代相关
1
2
OC:老年代的大小(字节)
OU:老年代的已使用大小(字节)
  • 方法区相关
1
2
3
4
MC:方法区的大小
MU:方法区的已使用大小
CCSC:压缩类空间的大小
CCSU:压缩类空间的已使用大小
  • GC相关——如果发现两行之间的GC时间占总时间过长或者内存占用不断增加,说明可能有内存问题。
1
2
3
4
5
YGC:从app启动到采样时,发生的YGC次数
YGCT:从app启动到采样时,发生的YGC的消耗时间(秒)
FGC:从app启动到采样时,发生的FullGC次数
FGTC:从app启动到采样时,发生的FullGC的消耗时间(秒)
GCT:从app启动到采样时,发生的GC的总时间(秒)

image-20210521133405766

了解到这个程度,实战中如有需要,可以再进一步查看命令行手册:

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jstat.html#BEHHGFAE

image-20210521131317493

6.内存监控与分析-jmap/jhat

6.1.解决什么问题

  • jmap可以将内存信息导出为dump文件(也叫做堆转储快照文件)。
    • 当用jstat初步定位发现疑似内存泄露时,就可以通过jmap将内存快照导出出来,进一步定位。
  • jhat可以导入jmap导出的内存快照文件,进行相对可视化的查看。
    • jhat内置了一个微型的http服务器,用于查看dump文件的分析结果。
    • JDK11已经删除了jhat,官方推荐用VisualVM替代。

6.2.参数

  • jmap的参数格式如下:
1
jmap [option] <pid>
  • **例子:**jmap -dump:format=b,file=<filepath.hprof> ——手动导出某个时间点的内存快照,会触发1次FullGC,dump文件中会保存FullGC后留下的对象信息,对于大内存镜像会比较耗时。
  • **例子:**java -jar xxx.jar -Xmx100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<filepath.hprof>——当待定位的系统发生OOM的时候出现闪退时,此时会自动导出dump文件。

当然,如何分析dump文件是一个更大的话题,后续再展开。仅针对jmap命令行了解到这个程度,实战中如有需要,可以再进一步查看命令行手册:

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jmap.html#CEGCECJB

image-20210521135354519

7.线程监控与分析-jstack

7.1.解决什么问题

jstack可以生成虚拟机指定进程当前时刻的线程快照,用于定位线程长时间停顿的原因。

7.2.参数

  • jstack的参数格式如下:
1
jstack <pid>
  • **例子:**jstack ——如果程序中有线程死锁,会在dump文件中体现出来。

image-20210521140136121

了解到这个程度,实战中如有需要,可以再进一步查看命令行手册:

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jstack.html#BABGJDIF

image-20210521135529140

8.总结

本文主要内容:

  • JVM性能监控与调优的工具有两种流派:命令行工具、可视化工具。
  • 学习命令行工具集的内在逻辑:工具间的关系、每个工具解决的问题、每个工具的参数分类。
  • 进程监控工具:jps
  • 配置查询工具:jinfo
  • JVM状态监控工具:jstat
  • 内存监控与分析工具:jmap、jhat(jdk11用VisualVM替代)
  • 线程监控与分析工具:jstack

9.参考文献

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/