序言
本文旨在总结Java开发中常用的监控工具与故障排查工具,从而提高监控JVM、排查JVM相关问题的效率。
TIPS
本系列文章基于JDK 11编写。但总的来说,JDK 11和JDK8 的差异并不大,绝大多数对JDK 8也可适用。当遇到不适用的命令时,可前往对应命令中的JDK 8的地址查看相关文档即可。
JDK内置工具
内置工具包括了JDK中提供的常用监控工具以及故障排查工具,主要包括了∶
监控工具
- jps
- jstat
故障排查工具
- jinfo
- jmap
- jstack
- jcmd
- jhat
- jhsdb
可视化工具
- jhsdb
- jconsole
- VisualVM
- JDK Mission Control
这些工具从可用性以及授权的不同,主要可以分为三类∶
- 正式支持工具∶表示这类工具会有
长期的技术支持,不同的平台、不同的JDK版本之间,这些工具可能会有一定差异,但总体来说还是比较兼容的。 - 实验性工具∶这类工具会被声明是
实验性质,不会有技术支持,一些工具甚至可能会在某个新的JDK版本中突然就消失了。不过这些命令其实也都非常稳定,而且功能很强大,也是可以用在生产的。在实际项目中定位问题发挥的作用也非常的大。 - 商业授权工具∶指的主要是
JMC以及JMC需要用到的JFR,这些工具在商业环境中使用的话是要付费的,但一般来说在个人开发环境中使用是免费的。
第三方工具
- Memory Analyzer Tool
- JITWatch
当然除列出的工具外,还有很多其他的工具,比如
JProfiler等.
详解
1、内置监控工具-jps
jps全称Java Virtual Machine Process Status Tool,用来查看JVM进程状态。
TIPS
此命令是实验性的,不受支持。
参考文档∶
- Java 8: docs.oracle.com/javase/8/do…
- Java 11: docs.oracle.com/en/java/jav…
使用说明
命令如下∶
$ jps -h
usage: jps [--heLp]
jps [-q] [-mlvV] [<hostid>]
Definitions:
<hostid>:
<hostname>[:<port>]
-?-h --help -help:Print this help message and exit
参数如下∶
- -q: 只显示进程号
- -m: 显示传递给
main方法的参数 - -l: 显示应用
main class的完整包名应用的jar文件完整路径名 - -v: 显示传递给
JVM的参数 - -V:
禁止输出类名、JAR文件名和传递给main方法的参数,仅显示本地JVM标识符的列表。 - hostid∶想要查看的
主机的标识符,格式为∶[protocol:][[//]hostname][∶port][servername,其中∶- protocol∶通信协议,默认
rmi - hostname∶目标主机的
主机名或IP地址 - port∶ 通信
端口,对于默认 rmi 协议,该参数用来指定 rmi registry 远程主机上的端口号。如省略该参数,并且该protocol指示rmi,则使用默认使用1099端口。 - servicename∶
服务名称,取值取决于实现方式,对于rmi协 议,此参数代表远程主机上RMI远程对象的名称
- protocol∶通信协议,默认
使用示例
- jps
- jps -m
- jps -ml
- jps -mlv
等同于
ps -ef | grep java
- 查看remote.domain这台服务器中JVM进程的信息,使用rmi协议,端口1099
jps - remote.domain
- 查看remote.domain这台服务器中JVM进程的信息,使用rmi协议,端口1231
jps -l rmi://remote.comain:1231
jps未找到该命令解决方式
$ jps
-bash: jps: 未找到命令
一般情况下,如果服务器上使用的是openJDK,则有可能报此错误。只需单独安装一个openjdk-devel包即可。尽量安装与你JDK版本一致的包,避免不必要的麻烦。
$ sudo yum install java-11-openjdk-devel-11.0.10.0.9-0.el7_9.x86_64
2、 内置监控工具-jstat
jstat全称JVMStatistics Monitoring Tool,用于监控JVM的各种运行状态。
TIPs
此命令是实验性的,不受支持。
参考文档∶
- Java 8: https:/docs.oracle.com/javase/8/docs/technotes/tools/unix/st.tml
- Java 11:https:/docs.oracle.com/en/java/javase/ll/tools/jstat.tml
使用说明
命令如下∶
jstat -h
Usage: jstat --help|-options
jstat -<option>[-t][-h<lines>] <vmid>[<interva
-h requires an integer argument
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
Definitions:
<option> An option reported by the -options option
指定参数,取值可用jstat -options查看
<vmid> Virtual Machine Identifier. A vmid takes the following
VM标识,格式为<lvmid>[@<hostname>[:<port>]]
form:
<lvmid>[@<hostname>[:<port>]]
Where <lvmid> is the local vm identifier for the target
Java virtual machine, typically a process id;
<lvmid> 如果Lvmid是本地VM,那么用进程号即可;
<hostname> is the name of the host running the target Java virtual machine;
<hostname>∶目标JVM的主机名;
and <port> is the port number for the rmiregistry on the
target host.
<port>∶目标主机的rmiregistry端口;
See the jvmstat documentation for a more complete
description of the Virtual Machine Identifier.
-t 用来展示每次采样花费的时间
<lines> Number of samples between header lines.
每抽样几次就列一个标题,默认0,显示数据第一行的列.
<interval> Sampling interval. The following forms are allowed:
<n>["ms"|"s"]
Where <n> is an integer and the suffix specifies the units as
milliseconds("ms") or seconds("s"). The default units are "ms".
抽样的周期,格式使用∶<n>【"ms"|"s"】,n是数字, 默认是毫秒。
<count> Number of samples to take before terminating.
采样多少次停止。
-J<flag> Pass <flag> directly to the runtime system.
将<flag>传给运行时系统,例如∶-J-Xms48m
option取值如下∶
- class∶ 显示类加载器的统计信息
- compiler∶显示有关Java HotSpot VM即时编译器行为的统计信息
- gc∶显示有关垃圾收集堆行为的统计信息
- gccapacity∶统计各个分代(新生代,老年代,持久代)的容量情况
- gccause∶ 显示引起垃圾收集事件的原因
- gcnew∶显示有关新生代行为的统计信息
- gcnewcapacity∶显示新生代容量
- gcold∶ 显示老年代、元空间区的统计信息
- gcoldcapacity∶显示老年代的容量
- gcmetacapacity∶显示元空间的容量
- gcutil∶ 显示有关垃圾收集统计信息的摘要
- printcompilation∶显示Java HotSpot VM编译方法统计信息
输出信息
- class: 类信息相关
- Loaded∶ 当前加载的类的数量
- Bytes∶当前加载的空间(单位KB)
- Unloaded∶卸载的类的数量Number of classes unloade d.
- Bytes∶当前卸载的空间(单位KB)
- Time∶执行类加载/卸载操作所花费的时间
- compiler: 编译信息相关
- Compiled∶执行了多少次编译任务
- Failed∶多少次编译任务执行失败
- Invalid∶无效的编译任务数
- Time∶ 执行编译任务所花费的时间
- FailedType∶上次失败的编译的编译类型
- FailedMethod∶上次失败的编译的类名和方法
- gc: GC概述
- SOC∶第一个存活区(S0)的容量(KB)
- S1C∶第二个存活区(S1)的容量(KB)
- SOU∶第一个存活区(SO)使用的大小(KB)
- S1U∶第二个存活区(S1)使用的大小(KB)
- EC∶伊甸园空间容量(KB)
- EU∶伊甸园使用的大小(KB)
- OC∶老年代容量(KB)
- OU∶老年代使用的大小(KB)
- MC∶元空间的大小(KB)
- MU∶元空间使用的大小(KB)
- CCSC∶压缩的类空间大小(KB)
- CCSU∶压缩类空间使用的大小(KB)
- YGC∶年轻代垃圾收集事件的数量
- YGCT∶年轻代垃圾回收时间
- FGC∶Full GC事件的数量
- FGCT∶Full GC回收时间
- GCT∶ 垃圾收集总时间
- gccapacity: GC容量概述
- NGCMN∶最小新生代容量(KB)
- NGCMX∶最大新生代容量(KB)
- NGC∶ 当前的新生代容量(KB)
- SOC∶第一个存活区(SO)的当前容量(KB)
- SIC∶第二个存活区(SI)的当前容量(KB)
- EC∶当前伊甸园容量(KB)
- OGCMN∶最小老年代容量(KB)
- OGCMX∶最大老年代容量(KB)
- OGC∶当前老年代容量(KB)
- OC∶当前old space容量(KB)
- MCMN∶最小元空间容量(KB)
- MCMX∶最大元空间容量(KB)
- MC∶当前元空间的容量(KB)
- CCSMN∶压缩的类空间最小容量(KB)
- CCSMX∶压缩的类空间最大容量(KB)
- CCSC∶ 当前压缩的类空间大小(KB)
- YGC∶年轻代GC事件的数量
- FGC∶Full GC事件的数量
- gccause∶gc原因,其他展示列和-gcutil一致
- LGCC∶ 导致GC的原因
- GCC∶导致当前GC的原因
- gcnew: 新生代GC信息
- SOC∶第一个存活区(SO)的容量(KB)
- S1C∶第二个存活区(SI)的容量(KB)
- SOU∶第一个存活区(S0)的利用率(KB)
- S1U∶第二个存活区(S1))的利用率(KB)
- TT∶老年代阈值
- MTT∶最大老年代阈值
- DSS∶期望的存活区大小(KB)
- EC∶ 当前伊甸园容量(KB)
- EU∶伊甸园利用率(KB)
- YGC∶年轻代GC事件的数量
- YGCT∶年轻代垃圾回收时间
- gcnewcapacity: 新生代GC容量信息
- NGCMN∶最小年轻代容量(KB)
- NGCMX∶最大年轻代容量(KB)
- NGC∶当前年轻代容量(KB)
- S0CMX∶最大S0容量(KB)
- SOC∶ 当前S0容量(KB)
- S1CMX∶最大S1容量(KB)
- SIC∶当前S1容量(KB)
- ECMX∶最大伊甸园容量(KB)
- EC∶ 当前伊甸园容量(KB))
- YGC∶年轻代GC事件的数量
- FGC∶Full GC事件的数量
- gcold: 老年代GC问题
- MC∶当前元空间使用大小(KB)
- MU∶元空间利用率(KB)
- CCSC∶ 压缩的类的大小(KB)
- CCSU∶使用的压缩类空间(KB)
- OC∶当前的老年代空间容量(KB)
- OU∶来年代空间利用率(KB)
- YGC∶年轻代GC事件的数量
- FGC∶Full GC事件的数量
- FGCT∶ Full GC垃圾收集时间
- GCT∶总垃圾收集时间
- gcoldcapacity: 老年代容量问题
- OGCMN∶最小老年代容量(KB)
- OGCMX∶最大老年代容量(KB)
- OGC∶当前老年代容量(KB)
- OC∶当前old spacc容量(KB)
- YGC∶年轻代GC事件的数量
- FGC∶ Full GC事件的数量
- FGCT∶ Full GC垃圾收集时间
- GCT∶ 总垃圾收集时间
- gcmetacapacity: 元空间信息
- MCMN∶最小元空间容量(KB)
- MCMX∶最大元空间容量(KB)
- MC∶元空间大小(KB)
- CCSMN∶压缩的类空间最小容量(KB)
- CCSMX∶压缩的类空间最大容量(KB)
- YGC∶年轻代GC事件的数量
- FGC∶Full GC事件的数量
- FGCT∶Full GC垃圾收集时间
- GCT∶总垃圾收集时间
- gcutil: GC util
- S0∶第一个存活区(S0)利用率
- S1∶ 第二个存活区(S1)利用率
- E∶Eden空间利用率
- O∶老年代空间利用率
- M∶元空间利用率
- CCS∶压缩的类空间利用率
- YGC∶年轻代GC事件的数量
- YGCT∶年轻代垃圾回收时间
- FGC∶Full GC事件的数量
- FGCT∶ Full GC垃圾收集时间
- GCT∶总垃圾收集时间
- printcompilation: 打印编译相关信息
- Compiled∶ 由最近编译的方法去执行的编译任务数
- Size∶最近编译的方法的字节码的字节数
- Type∶最近编译的方法的编译类型。
- Method∶标识最近编译的方法的类名和方法名。类
名使用/代替点.作为名称空间分隔符;方法名称是指定类中的方法。这两个字段的格式与
HotSpot -XX∶+PrintCompilation选项一致。
使用示例
- 示例1∶查看21110这个进程的gc相关信息,每隔250ms采样1次,采样7次
jstat -gcutil 21110 250 7
- 示例2∶显示有关新生代行为的统计信息,重复列标题∶
jstat -gcnew -h3 21110 250
- 示例3∶查看remote.domain机器上的40496这个进程有关垃圾收集统计信息的摘要,每隔1秒采样1次∶
jstat -gcutil 40496@remote.domain 1000
3、内置故障排查工具-jinfo
jinfo全称Java Configuration Info,主要用来查看与调整JVM参数。
TIPS
- 此命令是实验性的,不受支持,对于JDK9及更高版本
,部分功能可使用
jhsdb jinfo代替,也可用jcmd代替。 - 部分JDK版本的
jinfo命令对Windows支持比较有限,参数较少。如果在Windows操作系统下测试,应以jinfo -h的结果为准。
参考文档∶
- Java 8 Unix:docs.oracle.com/javase/8/do…
- JDK8 Windows: docs.oracle.com/javase/8/do…
- Java 11: docs.oracle.com/en/java/jav…
使用说明
命令如下∶
$ jinfo -h
Usage:
jinfo [option] <pid>
(to connect to running process)
jinfo [option] <executable <core>
(to connect to a core file)
jinfo [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)
where <option> is one of:
-flag <name> to print the value of the named VM flag
打印指定参数的值
-flag [+|-]<name> to enable or disable the named VM flag
启用/关闭指定参数
-flag <name>=<value> to set the named VM flag to the given value
将指定的参数设置为指定的值
-flags to print VM flags
打印VM参数
-sysprops to print Java system properties
打印系统属性
<no option> to print both of the above
打印VM参数及系统属性
-h | -help to print this help message
使用示例查看参数
- 示例1∶打印21110这个进程的VM参数及Java系统属性∶
jinfo 21110
- 示例2∶打印21110这个进程的Java系统属性
jinfo -sysprops 21110
- 示例3∶打印21110这个进程的VM参数
jinfo -flags 21110
- 示例4∶打印21110这个进程ConcGCThreads参数的值
jinfo -flag ConcGCThreads 21110
拓展知识
要想查看JVM参数,也可在启动时,指定-XX∶+PrintFlagsFinal,这样会在启动时将JVM参数打印到日志。
动态修改参数
我们先jinfo -flags 21110查看下面具体的参数。
- 示例5∶将21110这个进程的PrintClassHistogram设置为false
jinfo -flag -PrintClassHistogram 21110
- 示例6∶ 将21110这个进程的MaxHeapFreeRatio设置为80
jinfo -flag MaxHeapFreeRatio=80 21110
TIPS
虽然可用jinfo动态修改VM参数,但并非所有参数都支持动态修改,如果操作了不支持的修改的参数,将会报类似如下的异常∶
Exception in thread "main" com.sun.tools.attach.At tach0perationFailedException:flag 'xxx' cannot be changed
使用如下命令显示出来的参数,基本上都是支持动态修改的
java -XX:+PrintFlagsInitial | grep manageable
4、内置故障排查工具-jmap
jmap全称Java Memory Map,用来展示对象内存映射或堆内存详细信息。
TIPS
- 此命令是实验性的,不受支持,对于JDK9及更高版本
,部分功能可使用
jhsdb jmap代替,也可用jcmd代替。 - 部分JDK版本的
jmap命令对Windows支持比较有限,参 数较少。如果在Windows操作系统下测试,应以jmap -h的结果为准。
参考文档∶
- Java 8 Unix: docs.oracle.com/javase/8/do…
- JDK8 Windows: docs.oracle.com/javase/8/do…
- Java 11:https:/docs.oracle.com/en/java/javase/11/tools/jmap.html
使用说明
命令如下∶
$ jmap -h
Usage:
jmap -clstats <pid>
to connect to running process and print class loader statistics
jmap -finalizerinfo <pid>
to connect to running process and print information on objects awaiting finalization
jmap -histo[:live] <pid>
to connect to running process and print histogram of java object heap
if the "live" suboption is specified, only count live objects
jmap -dump:<dump-options> <pid>
to connect to running process and dump java heap
jmap -? -h --help
to print this help message
dump-options:
live dump only live objects; if not specified,
all objects in the heap are dumped.
format=b binary format
file=<file> dump heap to <file>
Example: jmap -dump:live,format=b,file=heap.bin <pid>
命令格式∶
jmap [options] pid
options的可选项如下∶
- -clstats∶ 连接到正在运行的进程,并打印Java堆的类加载器统计信息
- -finalizerinfo∶ 连接到正在运行的进程,并打印等待finalization的对象的信息
- -histo[∶live]∶ 连接到正在运行的进程,并打印Java堆的直方图。如果指定了live子选项,则仅统计活动对象
- -dump:∶连接到正在运行的进程,并转储Java堆。其中,dump_options的取值为∶
- live∶指定时,仅Dump活动对象;如果未指定,则转储堆中的所有对象
- format=b∶ 以hprof格式Dump堆
- file=filename∶将堆Dump到filename
使用示例
- 展示21110进程的类加载统计信息
jmap -clstats 21110
- 展示21110进程中等待finalization的对象的信息
jmap -finalizerinfo 21110
- 展示21110进程中堆的直方图
jmap -histo 21110
- 展示21110进程堆中存活对象的直方图
jmap -histo:live 21110
- Dump 21110这个进程中的存活对象的堆到dump.hprof文件
jmap -dump:live,format=b,file=dump.hprof 21110
拓展知识
要想获取Java堆Dump,除使用jmap外,还有以下方法∶
- 使用
-XX∶+HeapDumpOnOutOfMemoryError,让虚拟机 在OOM异常出现后自动生成堆Dump文件; - 使用
-XX∶+HeapDumpOnCtrlBreak,Windows可使用[Ctrl]+[Brea k],让虚拟机生成堆Dump文件; - 在Linux操作系统下,发送
kill -3 pid命令
spring boot应用且使用了
nohup命令启动应用,则生成的堆Dump文件就在jar包所在路径的nohup.out文件中
- 对于Spring Boot应用,也可以使用
Spring Boot Actuator提供的/actuator/heapdump实现堆Dump。浏览器访问该地址,会自动下载堆Dump文件
5、内置故障排查工具-jstack
jstack,全称Stack Trace for Java,用于打印当前虚拟机的线程快照(线程快照也叫Thread Dump或者javacore文件)。
TIPS
- 此命令是实验性的,不受支持,部分功能可用 jhsdb jstack 代替。
- 不同版本参数不同(JDK8有-m、-F参数等,JDK 11都没了)
参考文档∶
- Java 8 Unix: docs.oracle.com/javase/8/do…
- Java 8 Windows: docs.oracle.com/javase/8/do…
- Java 11: docs.oracle.com/en/java/jav…
使用说明
命令如下∶
# jstack -h
Usage:
jstack [-l][-e] <pid>
(to connect to running process)
Options:
-l long listing. Prints additional information about locks
-e extended listing. Prints additional information about threads
-? -h --help -help to print this help message
Options:
- -l: 显示有关锁的额外信息
- -e: 展示有关线程的额外信息(比如分配了多少内存、定义了多少个类)
使用示例
jstack 21110
jstack -l 21110
jstack -l -e 21110
6、内置故障排查工具-jhat(了解)
jhat全称JVM Heap Analysis Tool,用来分析jmap生成的堆Dump。
TIPS
- 此命令是实验性的,不受支持。
- jhat功能不是很强,
VisualVM、Eclipse Memory Analyzer等都比jhat强大,建议优先使用jhat的替代工具。
参考文档∶
- Java 8 Unix: docs.oracle.com/javase/8/do… otes/tools/unix/jhat.html
- Java 8 Windows: docs.oracle.com/javase/8/do… chnotes/tools/windows/jhat.html
- Java 11∶
已废弃
使用说明
命令格式∶
jhat [options] heap-dump-file
options的可选项如下∶
- -stack false|true∶
开启或关闭跟踪对象分配调用栈,默认true。 - -refs false |true∶
开启或关闭对对象引用的跟踪,默认true - -port port-number∶ 指定
jhat HTTP Server的端口,默认7000 - -exclude exclude-file∶ 指定一个文件,该文件列出了应从可
达对象查询中排除的数据成员。例如,如果文件包含
java.lang.String.value,则对于指定对象o,不管对象列表针对o是否可达,都不会考虑涉及java.lang.String.value的引用路径 - -baseline exclude-file∶ 指定
基线堆Dump文件。两个堆Dunm p中,具有相同对象ID的对象都会标记为不是新对象,其他对象被标记为新对象。这对于比较两个不同的堆转储很有用 - -debug intSets∶ 指定该工具的
debug级别。设置为0,则不会 有debug输出。数值越高,日志越详细 - -version∶ 显示版本
使用示例
- 分析1.hprof,并开启对象分配调用栈的分析
jhat -stack true 1.hprof
- 分析1.hprof,开启对象分配调用栈的分析,关闭对象引用的分析
jhat -stack true -refs false 1.hprof
等待片刻之后,访问 http∶//localhost∶7000/即可查看分析结果。
7、内置故障排查工具-jcmd
jcmd全称JVM Command,用于将诊断命令请求发送到正在运行的J ava虚拟机,从JDK 7开始提供。
参考文档∶
- Java 8 Unix: docs.oracle.com/javase/8/do…
- Java 8 Windows:docs.oracle.com/javase/8/do…
- Java 11: docs.oracle.com/en/java/jav…
使用说明
命令如下∶
$ jcmd-h
Usage: jcmd <pid | main class> <command ...|PerfCounter.print|-f file>
or: jcmd -l
or: jcmd -h
command must be a valid jcmd command for the selected jvm.
Use the command "help" to see which commands are available.
If the pid is 0, commands will be sent to all Java processes.
The main class argument will be used to match (either partially
or fully) the class used to start Java.
If no options are given, lists Java processes (same as -l).
PerfCounter.print display the counters exposed by this process
-f read and execute commands from the file
-l list JVM processes on the local machine
-? -h --help print this help message
参数说明
- pid∶ 接收诊断命令请求的进程ID。
- main class∶接收诊断命令请求的进程的
main类。jcmd会将诊断命令请求发送给具有指定main class的所有Java进程。 - command∶
command必须是一个有效的jcmd命令,可使用j cmd pid help命令查看可用的命令列表。如果pid是0.那么command将会被发送给所有Java进程。main class会用来去匹配(局部匹配或全量匹配)。如果未指定任何选项,它将会列出正在运行的Java进程标识符以及用于启动该进程的main class和命令行参数(相当于使用了-l`参数) - PerfCounter.print∶打印指定
Java进程上可用的性能计数器。 - -f filename∶从指定文件中读取命令并执行。在
file中,每个 命令必须写在单独的一行。以"#"开头的行会被忽略。当所有行的命令被调用完毕后,或者读取到含有stop关键字的命令,将会终止对file的处理。 - -l∶查看所有的JVM进程。
jcmd不使用参数与jcmd -l效果相同。
使用示例
- 查看所有JVM进程
jcmd -l
- 打印指定进程上可用的
性能计数器(性能统计信息)
jcmd 21110 PerfCounter.print
- 打印指定启动类为org.elasticsearch.bootstrap.Elasticsearch的应用上可用的性能计数器
jcmd org.elasticsearch.bootstrap.Elasticsearch PerfCounter.print
- 打印指定进程的
代码缓存的布局和边界
jcmd 21110 Compiler.codecache
支持的命令
关于其支持的命令,可以参照官方文档。
8、内置故障排查工具-jhsdb
jhsdb全称Java Hotspot Debugger,Hotspot进程调试器,可用于从崩溃的JVM附加到Java进程或核心转储。
jhsdb是一款基于Serviceability Agent(可维护性代理,简写为SA)的调试工具。
Serviceability Agent是一个JDK组件,用于快照调试、性能分析以及深入了解Hotspot JVM/Hotspot JVM上执行的Java应用程序。
它的工作原理有点类似于Linux上的GDB或者Windows上的Windbg 。但尽管诸如GDB的本机调试器可用于检查JVM,但这类本机调试器对Hotspot中的数据结构没有内在了解,因此无法对正在执行的Java应用程序进行深入了解。jhsdb了解JVM关键组件(例如Java堆,堆的代,region,代码缓存等)的位置和地址范围。
参考文档
- Java 8∶无,Java9才正式引入。
- Java 11: docs.oracle.com/en/java/jav…
TIPS
尽管JDK8及更低版本不直接提供jihsdb命令,但依然其实也是可以使用jhsdb的,只需找到 JAVA_HOME/Lib 目录下的sa-jdi.jar文件,然后启动即可。步骤如下∶
- 修改环境变量JAVA_HOME(这里用export临时修改环境变量,当然也可永久修改)
export JAVA_HOME="/Library/Java/JavaVirtualMachine s/jdk1.8.0_201.jdk/Contents/Home"
- 为sa-jdi.jar授予执行权限
sudo chmod +x $JAVA_HOME/Lib/sa-jdi.jar
- 启动方式1∶使用交互式命令行调试器(相当于jhsdb clhsdb)
java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB
- 启动方式2∶使用交互式GUI调试器启动jhsdb(相当于jhsdb hsdb)
java -cp $AVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot. HSDB
使用说明
- 启动
交互式命令行调试器
jhsdb clhsdb [--pid pid | --exe executable --core coredump]
- 启动
远程调试服务器
jhsdb debugd [options](pid |executable coredump)[ser ver-id]
- 启动
交互式GUI调试器
jihsdb hsdb [--pid pid | --exe executable --core coredump]
打印堆栈并锁定信息
jhsdb jstack [--pid pid | --exe executable --core coredump] [options]
- 打印堆信息
jhsdb jmap [--pid pid | --exe executable --core coredump] [options]
- 打印基本的JVM信息
jhsdb jinfo [--pid pid | --exe executable --core coredump] [options]
- 打印性能计数器信息
jhsdb jsnap [options] [--pid pid | --exe executable --core coredump]
其中∶
- pid∶ 进程号
- server-id∶ 当多个调试服务器在同一远程主机上运行时使用的
可选唯一ID - executable∶从中生成核心转储的Java可执行文件
- coredump∶ jihsdb工具连接到的Dump文件
coredump介绍与开启方式∶https∶/www.cnblogs.com/A nker/p/6079580.html
- options∶命令行选项,和子命令有关。
TIPs
pid、-exe参数二选一必填
options说明
jhsdb clhsdb子命令
# 进入cLhsdb
$ jhsdb clhsdb --pid 21110
# 进入交互界面后,输入help,就会打印出所有可用的commands
assert true | false
attach pid | exec core
buildreplayjars [ all | app | boot ] | [ prefix ]
detach
dis address [length]
disassemble address
dumpcfg { -a | id }
dumpcodecache
dumpideal { -a | id }
dumpilt { -a | id }
dumpreplaydata { <address > | -a | <thread_id> }
echo [ true | false ]
examine [ address/count ] | [ address,address]
……………………
TIPs
不同版本支持的命令可能不同,这里列出的命令可能你的JVM并不一定能支持。 参考文档∶
- http:/cr.openjdk.java.net/~minqi/6830717/raw_files/new/agent/doc/clhsdb.html
- https:/github.com/AdoptOpen.DK/openjdk-idkl1/blob/master/src/jdk.hotspot.agent/doc/clhsdb.html
jhsdb hsdb子命令
# 图形化模式,和clhsdb功能对标
$ jhsdb hsdb --pid 21110
如果是Linux系统,需要系统自带图形化界面输出才能用。不然会报错:No X11 DISPLAY variable was set, but this program performed an operation which requires it.
jhsdb jinfo子命令
- -flags∶打印VM标志
- -sysprops∶打印Java系统属性
- 留空∶打印VM标志和Java系统属性
示例∶
- 打印21110进程的VM标志
jhsdb jinfo --flags --pid 21110
- 打印21110进程的系统属性
jhsdb jinfo --sysprops --pid 21110
jhsdb jmap子命令
- -heap∶打印Java堆的概要信息
- -binaryheap∶将Java堆以hprof格式Dump出来
- -dumpfile∶执行dump文件名
- -histo∶打印Java堆的直方图
- -clstats∶打印Java堆的类加载器统计信息
- -finalizerinfo∶打印等待finalization的对象的信息
示例∶
- 打印21110进程Java堆的直方图
jhsdb jmap --histo --pid 21110
- 将81033进程的Java堆dump到2.hprof
jhsdb jmap --binaryheap --dumpfile 2.hprof --pid 21110
jhsdb jstack子命令
- -locks∶ 打印
java.util.concurrent锁的信息 - -mixed∶尝试打印
Java栈与本地方法栈的信息(需操作系统支持)示例∶
# 打印81033锁的信息,并尝试打印Java栈与本地方法栈的信息
$ jhsdb jstack --locks --mixed --pid 21110
jhsdb jsnap子命令
- -all∶ 打印所有性能计数器的信息示例∶
jhsdb jsnap --all --pid 21110
相当于∶
jcmd 21110 PerfCounter.print
jhsdb debugd子命令
- server-id∶当多个调试服务器在同一远程主机上运行时使用的可选唯一ID 示例∶
jhsdb debugd --pid 21110
9、内置可视化工具-jconsole
jconsole全称Java Monitoring and Management Console,是一款基于JMX(Java Management Extensions)的可视化监控、管理工具。它主要通过JMX的MBean (Managed Bean)对系统进行信息收集和参数动态调整。
JMX是一种开放性的技术,它既可以用在虚拟机本身的管理上,也可以用于运行在虚拟机之上的软件中。目前很多软件都支持基干JMX进行管理与监控。
启动执行如下命令∶
jconsole
就会弹出jconsole一个新建连接的窗口。
- 对于本地JVM进程,jconsole会自动搜索踹,无需用户人工指定;
- 如果想要连接远程进程,需要远程应用开启了对应的JMX
界面解释
- 概览∶展示虚拟机运行数据的概要信息,包括堆内存使用量、线程、类、CPU占用率的曲线图。这些曲线图本质上是内存、线程、类等几个页面的信息汇总。
- 内存∶用于监控虚拟机内存的变化趋势,相当于可视化的jstat命令。
- 线程∶监控应用线程的个数波动及状态,当遇到线程停顿的时候可以考虑用这个页面的功能进行分析,相当于可视化的jstack命令。
- 类∶监控应用加载的类的变化趋势。
- VM概要∶展示应用的一些概要信息。
- MBean∶展示应用被JMX管理的Bean。
10、内置可视化工具-VisualVM
TIPS
- 本文基于
VisualVM2.0.1编写 - 不同版本的
VisualVM特性有所不同(例如VisualVM 2. 0+提供了对JDBC的性能分析),界面甚至也会有一定差异.
官方说VisualVM是一个All-in-One Java Troubleshooting Tool,从JDK6开始提供,是JDK8及之前最强大的监控及故障处理程序之一。
启动
JDK 8或更低版本
- 对于JDK8及更低版本,JDK内置了VisualVM。只需执行如下命令即可启动
jvisualvm
JDK9及更高版本
- 对于JDK9及更高版本,VisualVM默认不再内置,而是作为一个独立项目维护,因此需要手动下载。独立下载的VisualVM,最低支持JDK8。
- GitHub: github.com/oracle/visu…
- 官方网站∶ https∶/visualvm.github.io/
因此,需自行下载。前往 https://visualvm.github.io/download.html 即可下载。其中∶
- zip文件∶可适用于各种操作系统(
建议下载zip) - dmg文件∶ 可适用于macOS
下载完成后,解压,并按操作系统启动∶
- 对于非Windows系统∶运行如下命令即可启动
${visualvm目录}/bin/visualvm
- 对于Windows系统∶执行如下文件即可。
${visualvm目录}/bin/visualvm.exe
使用
- Overview∶展示应用的概要信息,相当于可视化的jps、jinfo
- Monitor∶监控
- 图表∶展示CPU、内存、类、线程等曲线图
- Perform GC∶通知JVM执行垃圾回收
- Heap Dump∶Dump堆,相当于jmap dump命令。点击后,在左侧的heapdump节点上右击另存为,可将其存储为文件。
- Threads∶查看线程状态
- Thread Dump∶Dump线程,相当于jstack。点击后,在左侧的threaddump节点上右击另存为,可将其存储为文件。
- Sampler∶抽样器,可用于实时性能分析
- CPU抽样∶ 可展示每个线程花费的CPU时间、分析热点方法等
- 内存抽样∶展示堆直方图、每个线程的内存分配
- Profiler∶性能分析,提供了程序运行期方法级的处理器执行时间分析及内存分析
- CPU性能分析、内存性能分析、JDBC性能分析
- 还可以配置想检查的范围。
- 注意点∶
- 执行性能分析,会对程序运行
性能有比较大的影响,一般不建议在生产环境使用这项功能。可在开发/测试环境去分析并调优,也可用JMC代替,JMC的性能分析能力更强,而且影响相对小很多。 - 类共享(类共享是一种共享类,从而提升加载速度、节省内存的技术)可能会导致执行Profiler的应用崩溃,建议在执行Profiler的应用上添加-Xshare∶off,关闭掉类共享。
- 执行性能分析,会对程序运行
11、内置可视化工具-JDK Mission Control
JDK Mission Control
有关商业授权的说明∶
- JMC曾经是一款商业授权工具(例如在JDK 8中
),需要商业授权才能在生产环境中使用。但根据
Oracle Binary Code协议,在个人开发环境中可以免费使用。 - 现已开源,在JDK11(哪怕是OpenJDK)中,任何人都可以使用
JFR + JMC(需遵循UPL协议)! - 本文只调研了JDK的长期支持版本JDK 8/JDK11的商业授权规则,没有去细究短期支持版本JDK 9、10等短期支持版本的商业授权规则。
JDK Mission Control也叫Java Mission Control,简称JMC。
JMC的两大功能∶
- 作为JMX控制台,监控虚拟机MBean提供的数据
- 可持续收集数据的
JFR(Java Flight Recorder),并可作为JFR的可视化分析工具
在正式探讨JMC之前,有必要先聊下JFR。
JFR(Java Flight Recorder)是一种用于收集有关运行中的Java应用的诊断信息和性能数据的工具。它几乎没有性能开销,因此,即使在负载很大的生产环境中也可以使用。JFR主要用于以下场景∶
- 性能分析
JFR可连续捕获应用的信息。比如执行概要分析(显示程序花费时间的地方),线程停顿/等待时间概要分析(显示线程为什么不运行的原因),分配概要分析(显示分配压力的位置),垃圾回收详细信息等
- 黑盒分析
由于JFR开销非常低,因此可持续打开Flight Recorder,让JF R将信息保存到缓存区,然后在稍后再去分析这块数据,定位特定异常的原因
- 支持与调试
联系Oracle支持人员寻求帮助时,JFR收集到的数据可能至关重要
安装JMC
- 对于JDK10及更低版本,JDK内置了JMC,使用如下命令启动∶
jmc
- 对于JDK 11及更高版本
TIPS: 此方式也同样适用于JDK8
JMC独立出来并开源了,既可以下载独立软件,也可作为Eclipse的插件存在。但不管哪种方式,都需要手动下载。JMC相关地址∶
- 开源地址∶https∶/jkjava.net/jmc/
- 开源的Wiki∶ https∶//wiki.openjdkjava.net/display/mc/Main
- Oracle侧有关JMC的官方网站∶https∶/www.oracle.com/techne twork/java/javaseproducts/mission-control/index.html
下载
- 前往 htps://jdk.java.net/jmc/根据造自己的操作系统下载即可
- 下载完成后,参照https∶/www.oracle.com/technetwork… vase/jmc-install-6415206.html的说明安装即可,里面包含了JMC在操作系统、在各种场景下的安装。
使用JMC+JFR
如果想监控的应用所使用的JDK版本 < JDK 11
- 在启动你想监控的应用时,需添加如下参数∶ -XX:+UnlockCommerciaLFeatures -XX:+FlightRecorder 其中∶
- UnlockCommercialFeatures∶解锁商业特性
- FlightRecorder∶为应用启用或停用JFR(JDK 8u40开始,省略)
- 然后使用JMC连接该应用即可监控该应用 此外,还有其他相关参数可参考∶docs.oracle.com/javacompone…
如果想监控的应用所使用的JDK版本 >= JDK 11
- 被监控的应用无需设置JVM参数,直接启动即可。
- 使用JMC连接该应用即可监控该应用
功能说明
JMX
- 概览∶ 各种概要信息
- MBean浏览器∶展示应用被JMX管理的Bean
- 触发器∶配置触发规则,当规则满足时,就触发某个操作(在操作一栏配置)
- 系统∶查看系统相关信息
- 内存∶查看内存相关信息
- 线程∶查看线程相关信息
- 诊断命令∶可视化使用诊断命令,相当于可视化的jcmd
JFR
- 自动分析结果∶ JMC自动给出的优化提议
- Java应用程序∶展示应用的各种执行情况
- JVM内部∶展示JVM层面的执行情况
- 环境∶展示操作系统层面的执行情况
- 事件∶展示录制期间发生的事件
TIPS
各项指标的含义详见∶JMC-帮助-JDK Mission Control帮助
插件
帮助-安装新软件
JMC特点
优点∶JMC的主要优点在于对JFR的支持
- JFR在生产环境中对吞吐量的影响一般不会高于1%.
- JFR监控过程是可动态的,无需重启
- JFR监控过程对应用完全透明,无需修改应用的代码,也无需安装额外的插件或代理
- JFR提供的数据质量非常高,对监控、排查的参考价值更大
缺点
- JFR并不完全向后兼容。比如,在JDK 11里面生成的JFR文件,用早期的JMC(例如JMC 5.5)无法打开;
- JMC 7.0.1无法分析堆dump文件(hprof格式),但官方Wiki宣称支持分析堆dump文件
12、Memory Analyzer (MAT) (了解)
Memory Analyzer又名Memory Analyzer Tool,简称MAT,可以作为独立软件,也可作为Eclipse插件存在。它是一个快速且功能丰富的Java堆内存分析器,可帮助您查找内存泄漏并减少内存消耗。相关地址∶
- 官方网站∶https∶//www.eclipse.org/mat/
- 下载地址∶https∶//www.eclipse.org/mat/downloa… ● Wiki: https:/wikieclipse.org/MemoryAnalyzer#Getting a Hea p_Dump
- 使用文档∶ https∶/help.eclipse.org/2020-03/indexjsp,点击左侧
Memory Analyzer菜单
MAT的主要功能∶
- 找出内存泄漏的原因
- 找出重复引用的类和jar
- 分析集合的使用
- 分析类加载器
概念
Shallow vs. Retained Heap(浅堆与保留堆)
- 浅堆(Shallow Heap)∶一个对象消耗的内存。一个对象引用需要32或64 bit(取决于OS体系结构),每个Integer 4个字节,每个Long8个字节等。根据堆转储格式,这个大小也可能会被调整(例如,对齐为8bit),从而更好地模拟VM 的实际消耗量。
- X的保留集(Retained set)∶当X被垃圾回收时,由GC删除的对象集。同理,如果X没有被回收,那么该集合中的对象都会"保留下来"。
- X的保留堆(Retained heap)∶X的保留集中的所有对象的浅层大小的总和,即X保持活动的内存。换而言之,Retaine d heap指的是对象X的保留内存大小,即由于它的存活导致多大的内存没有被回收。
- 前导对象集的保留集(例如特定类的所有对象,或特定类加载器加载的所有类的所有对象,或者仅仅是一堆任意对象)前导对象不可达时,被释放的那些对象。
一般来说,对象的浅堆是对象在堆中的大小,而同一对象的保留大小是在垃圾回收对象时将释放的堆内存量。 Retained set包括这些对象以及只能通过这些对象访问的所有其他对象。保留大小是保留集中包含的所有对象的总堆大小。
- Dominator Tree(支配树)
MAT提供了对象图的支配树。通过将对象参考图转换为支配树,您可以轻松地识别最大的保留内存块以及对象之间的依赖关系。下面是术语的非正式定义
- X支配Y∶如果对象图中从起始(或Root)节点到Y的每条路径都必须经过X,那么就说对象X支配对象Y
- 直接支配者∶某个对象路径最短的支配者.支配树是在对象图的基础上建立的,在支配树中,每个节点都是其子节点的直接支配者。因此,基于支配树可以轻松看出对象之间的依赖关系。
支配者树具有以下重要属性∶
- 对象从属于X的子树(例如对象被X支配)就是X的Retained set 也就是说,X节点的子树是所有被X支配的节点集合,也就是X的Retained set;
- 如果X是Y的直接支配节点,那么支配X的节点也可以支配Y
- 支配树中的边并不直接对应于对象图中的对象引用
使用
- 独立软件版本∶前往 htps://www.eclipse.org/mat/downloa…
- Eclipse插件版本∶在Eclipse插件中心安装即可使用。
主要功能项
- inspector∶透视图,用于展示一个对象的详细信息,例如内 存地址、加载器名称、包名、对象名称、对象所属的类的父类、对象所属的类的加载器对象、该对象的堆内存大小和保留大小,gc root信息。下半部分展示类的静态属性和值、对象的实例属性和值、对象所属的类的继承结构。
- Heap Dump History∶列出最近分析过的文件
- 功能选择栏∶从左到右依次是∶概览、类直方图、支配树、 OQL查询、线程视图、报告相关、详细功能。其中概览就是上图的这个页面,其他则提供了一些更细致的分析能力。总的来说,功能上和VisualVM大同小异,但分析得更加细致。
- 饼图∶展示retained size对象占用比例
- Actions∶常用的内存分析动作
- Histogram∶列出内存中的对象,对象的个数及其大
小。点击后生成的报表∶
- Class Name ∶类名称,java类名
- Objects∶类的对象的数量,这个对象被创建了多少个
- Shallow Heap∶一个对象内存的消耗大小,不包含对其他对象的引用
- Retained Heap∶是shallow Heap的总和,也就是该对象被GC之后所能回收到内存的总和
- Dominator Tree∶列出最大的那些对象,以及他们为什么存活。
- Top Consumers∶打印最昂贵的对象,以内和包分组● Duplicate Classes∶检测被多个classloader加载的类
- Histogram∶列出内存中的对象,对象的个数及其大
小。点击后生成的报表∶
- Reports∶报表
- Leak Suspects∶自动分析内存泄漏的原因,并能直接定位到Class,找到可能导致内存泄露的代码行数。
- Top Components∶列出占用超过1%的组件的报告信息
- Step by Step:
- Top Components∶分析从属于指定包或者classloader的对象。
13、第三方工具- JITWatch
2-29第三方工具-JITWatch
JITWatch使用较为复杂,需对编译原理、汇编、机器码有所了解才能玩得比较好。而这些内容,本文不会展开深入讲解。本文主要还是为了给大家构建相对完整的工具链知识体系(毕竟JITWatch是在其领域中非常典型的工具)。实际项目中这个领域的调优做得还是比较少的,可以先留个印象,以后汇编、机器码、编译源码等知识积累足够了自然就会比较轻松了。
JITWatch是JIT编译器的日志分析器与可视化工具。可用来检查内联决策、热点方法、字节码以及汇编的各种细节。它经常和HSDIS配合使用。
- GitHub: https:/github.com/AdoptOpenJDK/jitwatch
- Wiki: https:/github.com/AdoptOpenJDK/jitwatch/wiki/Trouble shooting
安装HSDIS
TIPS
即使不安装HSDIS,也不会影响JITWatch的运行,但是不安装HSDIS的话,就无法使用JITWatch查看汇编相关内容了。
HSDIS是一个HotSpot虚拟机即时编译代码的反汇编插件,它包含在HotSpot虚拟机的源码当中,在Open.JDK的网站也可以找到单独的源码下载,但并没有提供编译后的程序。
安装
不同操作系统安装方式略有不同,但是总的步骤是一样的∶
- 准备好HSDIS文件(从源码编译HSDIS,或下载已编译好的HSDIS文件)
- 将HSDIS文件放置到JDK中指定的目录(详见HSDIS存放目录)从源码编译
这里只讨论了JDK8和JDK 11。事实上,从JDK9+开始,安装方式和JDK11—致。 HSDIS源码地址∶
- JDK 11: https:/hg.openjdk.java.net/jdk/jdk/archive/jdk-11+7.ta r.bz2/src/utils/hsdis
- JDK8: https:/hg.openjdk.java.net/jdk/jdk/archive/jdk8-b116.ta r.bz2/hotspot/src/share/tools/hsdis
安装说明
- JDK 11:https:/github.com/openjdk/jdk/treejdk-11+7/src/utils/hsdis
- JDK8: https:/github.com/openjdk/jdk/tree/jdk8-bl16/hotspot/src/share/tools/hsdis
HSDIS存放目录
- JDK 11: $JAVA_HOME/Lib/server/
- JDK8∶ $JAVA_HOME/jre/lib/server/
下载已编译完成的HSDIS文件
不同操作系统、不同JDK版本下的HSDIS文件不能通用,因此,务必搜索和你操作系统、JDK版本都一致的HSDIS文件!!!
- 可在CSDN上搜索各版本的HSDIS文件
- 在 GitHub 上有Windows环境下的编译说明,并提供了hsdis的编译后文件
macOS JDK 11下编辑-源码安装HSDIS全过程
-
- 下载并解压hsdis
-
- 将目录切换到hsdis目录cd srC/utils/hsdis
-
- 在此目录中下载binutils,官方网站∶sourceware.org/binutils/
wget ftp.heanet.ie/mirrors/ftp… ils/binutils-2.28.tar.gz
-
- 解压binutilS
tar -xzf binutils-2.28.tar.gz
- 5.编译HSDIS
make BINUTILS=binutils-2.28 ARCH=amd64
- 6.将hsdis-amd64.dylib拷贝到$JAVA_HOME/lib/server下
sudo cp build/macosx-amd64/hsdis-amd64.dylib $JAVA_HOME /Lib/server/
启动应用
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp-XX:+LogCompilation -XX:LogFile=/Users/itmuch.co m/logfile.log -XX:+TraceClassLoading -jar xxx.jar
其中∶
- UnlockDiagnosticVMOptions∶开启诊断信息
- PrintAssembly∶输出反汇编内容
- Xcomp∶ 以编译模式启动,这样,无执行足够次数来预热即 可触发即时编译
- LogCompilation∶打印编译相关信息
- LogFile∶ 指定日志文件
- TraceClassLoading∶是否跟踪类的加载
执行完后,将会生成一个 /Users/itmuch.com/logfile.log 文件 里面包括了各种类编辑以及汇编信息
使用JITWatch可视化阅读日志
-
- 下载JITWatch源码,并切换到JITWatch的源码根目录(如果不会用Git,也可点击
https://github.com/AdoptOpenJDK/jitwatch/archive/master.zip下载源码)
- 下载JITWatch源码,并切换到JITWatch的源码根目录(如果不会用Git,也可点击
git clone https://github.com/AdoptOpenJDK/jitwatch.git
cd jitwatch
-
- 启动JITWatch
mvn clean compile exec:java
-
- 点击Open Log,并选择上面的日志文件
-
- 点击Config,并配置源码目录及JDK src
-
- 点击Start,即可可视化地分析了。