JVM(3):性能监控和故障处理
1. 基础故障处理工具
基础故障处理工具是JDK
自带的一些位于 $/bin$ 目录下的小工具。这些工具主要用于监视虚拟机运行状态和进行故障处理,根据软件可用性和授权的不同,可以分为三类:
- 商业授权工具:主要是
JMC
( $Java\ \ Mission\ \ Control$ ) 及其使用的JFR
( $Java\ \ Flight\ \ Recorder$ )。自JDK 7
开始集成,在JDK 11
前都无需独立下载,但是商业使用需要收费。 - 正式支持工具:长期支持的工具,可能在不同平台和版本之间存在差异。
- 实验性工具:没有技术支持,具有实验性质的工具,但通常很稳定且具有强大功能。
1.1 jps
:虚拟机进程状况工具
jps
( $JVM\ \ Process\ \ Status\ \ Tool$ ) 的名字类似于UNIX
系统中的 $ps$ 命令,功能也和其类似:列出正在运行的虚拟机进程,并显示主类名称和本地虚拟机唯一ID
( $Local\ \ Virtual\ \ Machine\ \ Identifier$, $LVMID$ )。作为使用频率最高的JDK
命令行工具,是其他工具查询进程的方法。对于本地虚拟机进程来说,LVMID
与操作系统进程ID
( $Process\ \ Identifier$, $PID$ ) 是一致的,使用Windows
任务管理器也可以查询到。但是如果存在多个虚拟机进程,使得无法通过进程定位时,jps
命令显示的主类信息就可以帮助我们定位。
jps [options] [hostid]
$options$ | 作用 |
---|---|
$-q$ | 输出LVMID ,省略主类信息 |
$-m$ | 输出主类 $main$ 函数参数 |
$-l$ | 输出主类全名,如果为JAR 包则输出路径 |
$-v$ | 输出JVM 参数 |
$hostid$ 为RMI
注册表中注册的主机名,jps
可以通过RMI
协议查询开启RMI
服务的远程虚拟机进程状态。
1.2 jstat
:虚拟机统计信息监视工具
jstat
( $JVM\ \ Statistics\ \ Monitoring\ \ Tool$ ) 用于监视本地或者远程虚拟机的各种运行状态信息,包括类加载、内存、垃圾收集、即时编译等。
jstat [option vmid [interval [s|ms] [count]]]
如果为本地虚拟机进程,那么 $vmid$ 就是LVMID
。如果是远程,那么格式应为:
[protocol:][//]lvmid[@hostname[:port]/servername]
$interval$ 和 $count$ 标识查询间隔和次数,默认为一次。在不加单位时,$interval$ 默认以 $ms$ 为单位。
通过 $jstat\ \ options$ 命令可以查询选项,选项对应的作用如下:
$options$ | 作用 |
---|---|
$-class$ | 监视类加载、卸载数量,类的总空间,以及类装载时间 |
$-compiler$ | 输出即时编译器编译过的方法、耗时等信息 |
$-gc$ | 监视Java 堆中各个区域的容量、使用情况和垃圾收集时间等 |
$-gccapacity$ | 与 $-gc$ 基本相同,但是输出更关注各个区域使用的最大和最小空间 |
$-gccause$ | 与 $-gcutil$ 基本相同,但是会输出上次垃圾收集的原因 |
$-gcmetacapacity$ | 输出元数据空间信息 |
$-gcnew$ | 监视新生代垃圾收集状况 |
$-gcnewcapacity$ | 与 $-gcnew$ 基本相同,但是输出更关注使用的最大和最小空间 |
$-gcold$ | 监视老年代垃圾收集状况 |
$-gcoldcapacity$ | 与 $-gcold$ 基本相同,但是输出更关注使用的最大和最小空间 |
$-gcutil$ | 与 $-gc$ 基本相同,但是输出更关注已使用的空间占比 |
$-printcompilation$ | 输出已被即时编译的方法 |
使用 $jcstat\ \ -gcutil\ \ [vmid]$ 获得了一次结果如下:
$S0$ | $S1$ | $E$ | $O$ | $M$ | $CCS$ | $YGC$ | $YGCT$ | $FGC$ | $FGCT$ | $GCT$ |
---|---|---|---|---|---|---|---|---|---|---|
$100.00$ | $0.00$ | $63.72$ | $72.36$ | $95.44$ | $93.29$ | $190$ | $0.966$ | $10$ | $1.028$ | $1.994$ |
$E$ 表示Eden
区,使用了 $63.72$% 的空间;$S0$ 和 $S1$ 表示两个Survivor
区,分别使用了 $100.00$% 和 $0.00$% ;$O$ 表示老年代,使用了 $72.36$% ;$M$ 表示方法区,使用了 $95.44$% ;$YGC$ 是程序运行以来进行Minor GC
的次数,为 $190$ 次;$YGCT$ 是Minor GC
的总耗时,为 $0.966$ 秒;$FGC$ 是程序运行以来的Full GC
次数,为 $10$ 次;$FGCT$ 是Full GC
总耗时,为 $1.028$ 秒;$GCT$ 是所有GC
的总耗时,为 $1.994$ 秒。
1.3 jinfo
:Java
配置信息工具
jinfo
( $Configuration\ \ Info\ \ for\ \ Java$ ) 用于实时查看和调整虚拟机的各项参数。虽然通过 $jps\ \ -v$ 就能看到JVM
参数,但是如果想要查看没有显式指定的系统默认值,就只能使用 $jinfo -flag$ 了。
jinfo [option] <pid>
$options$ | 作用 |
---|---|
$-flag\ \ <name>$ | 查询指定参数 |
$-flag\ \ [+/-]<name>$ | 启用或禁用指定参数 |
$-flag\ \ <name>=<value>$ | 设置指定参数值 |
$-flags$ | 查询所有参数 |
$-sysprops$ | 查询Java 系统属性,即 $System.getProperties(\ )$ 方法的值 |
1.4 jmap
:Java
内存映像工具
jmap
( $Memory\ \ Map\ \ for\ \ Java$ ) 用于生成堆转储快照 ( $heapdump$ / $dump$ ),类似功能也可以通过设置 $-XX:+HeapDumpOnOutOfMemoryError$ 实现。此外,也可以通过 $-XX:+HeapDumpOnCtrlBreak$ 实现通过 $Ctrl+Break$ 键生成堆转储快照文件。在Linux
系统下还可以通过 $kill\ \ -3$ 命令得到。除了获取堆转储快照外,jmap
还可以查询 $finalize$ 执行队列、Java
堆和方法区的详细信息。
jmap [option] <pid>
$options$ | 作用 |
---|---|
$-heap$ | 显示Java 堆信息 |
$-histo$ | 显示堆中对象的统计信息,如类和实例数量、容量等 |
$-clstats$ | 显示类加载器信息 |
$-finalizerinfo$ | 在F-Queue 中等待执行 $finalize$ 的对象 |
$-dump$ | 生成堆转储快照,格式为 $-dump:[live,]format=b,file=<filename>$ ,分别表示是否去除死亡对象,二进制格式和文件名 |
$-F$ | 强制生成堆转储快照 |
jmap
有部分功能在Windows
下受限,除了 $-dump$ 和 $-histo$ 外都不能使用。
1.5 jhat
:虚拟机堆转储快照分析工具
jhat
( $JVM\ \ Heap\ \ Analysis\ \ Tool$ ) 与jmap
搭配使用,用于分析堆转储快照。jhat
内置了一个微型的Web
服务器,分析完成后可以直接在浏览器中查看。但是一般情况下是不会在服务器上分析堆转储快照的,因为分析工作会占用大量资源,而且现在已经有了专门的图形化工具用于分析堆转储快照,并且功能也比jhat
提供的要多。
1.6 jstack
:Java
堆栈跟踪工具
jstack
( $Stack\ \ Trace\ \ for\ \ Java$ ) 用于生成虚拟机当前时刻的线程快照 ( $threaddump$ / $javacore$ ),即每一条线程正在执行的方法集合。线程快照可以用于定位线程问题,如死锁、死循环和请求外部资源导致的长时间挂起等。
jstack [option] <vmid>
$options$ | 作用 |
---|---|
$-F$ | 强制输出线程堆栈 |
$-l$ | 除堆栈外,显示锁信息 |
$-m$ | 如果有调用本地方法,可以用于显示C/C++ 堆栈 |
在JDK 5
之后,通过 $java.lang.Thread.getAllStackTraces(\ )$ 方法可以获取所有线程的 $StackTraceElement$ 对象,从而实现jstack
的大部分功能。
2. 可视化故障处理工具
2.1 JHSDB
:基于服务性代理的调试工具
JDK
中提供了JCMD
和JHSDB
两个集成式的多功能工具箱,不仅整合了所有基础工具提供的而功能,而且还对他们进行了优化。
基础工具 | JCMD |
JHSDB |
---|---|---|
$jps\ \ -lm$ | $jcmd$ | $N/A$ |
$jmap\ \ -dump\ \ <pid>$ | $jcmd\ \ <pid>\ \ GC.heap_-dump$ | $jhsdb\ \ jmap\ \ –binaryheap$ |
$jmap\ \ -histo\ \ <pid>$ | $jcmd\ \ <pid>\ \ GC.class_-histogram$ | $jhsdb\ \ jmap\ \ –histo$ |
$jstack\ \ <pid>$ | $jcmd\ \ <pid>\ \ Thread.print$ | $jhsdb\ \ jstack\ \ –locks$ |
$jinfo\ \ -sysprops\ \ <pid>$ | $jcmd\ \ <pid>\ \ VM.system_-properties$ | $jhsdb\ \ info\ \ –sysprops$ |
$jinfo\ \ -flags\ \ <pid>$ | $jcmd\ \ <pid>\ \ VM.flags$ | $jhsdb\ \ jinfo\ \ –flags$ |
JHSDB
是一款基于服务性代理 ( $Serviceability\ \ Agent$, $SA$ ) 实现的进程外调试工具。服务性代理是HotSpot
虚拟机中一组用于映射Java
虚拟机运行信息的API
集合,将C++
数据抽象出Java
对象。通过服务性代理的API
,可以在一个独立的Java
虚拟机进程内分析其他HotSpot
虚拟机的内部数据,或者从转出快照中还原其运行细节。
在使用 $jps$ 获取到进程ID
后,可以通过
jhsdb hsdb --pid <pid>;
进入JHSDB
的图形化模式。
2.2 JConsole
:Java
监视与管理控制台
JConsole
( $Java\ \ Monitoring\ \ and\ \ Management\ \ Console$ ) 是一款基于JMX
( $Java\ \ Management\ \ Extensions$ ) 的可视化监视、管理工具,主要通过JMX
的MBean
( $Managed\ \ Bean$ ) 对系统进行信息收集和参数动态调整。
JConsole
的启动很简单,通过 $/bin$ 目录下的 $jconsole.exe$ 即可启动,在启动后会自动扫描虚拟机进程,也可以通过远程连接对远程虚拟机进行监控。
2.3 VisualVM
:多合一故障处理工具
VisualVM
( $All-in-One\ \ Java\ \ Troubleshooting\ \ Tool$ ) 是功能最强大的运行监视和故障处理程序之一。除了常规的运行监视和故障处理之外,它还提供了性能分析 ( $Profiling$ ) 等功能。VisualVM
基于NetBeans
平台开发工具,支持插件扩展。在官网上下载包后,可以直接导入。当然,VisualVM
也提供了自动安装功能,能够找到大部分插件。
VisualVM
支持生成和浏览堆转储快照。虽然也支持性能分析功能,但是因为对运行有较大影响,因此一般不在生产环境使用,或者改用更强大、影响更小的JMC
完成。通过插件,VisualVM
中可以导入BTrace
。BTrace
可以在不中断程序的前提下加入调试代码。
2.4 Java Mission Control
:可持续在线的监控工具
JFR
( $Java\ \ Flight\ \ Recorder$ ) 是一套内建在HotSpot
虚拟机里面的监控和基于事件的信息搜集框架,具有可持续在线 ( $Always-On$ ) 的特性。在监控过程中,它都是可动态的,不需要重启应用。JMC
( $Java\ \ Mission\ \ Control$ ) 使用JMX
协议与虚拟机进行通信,显示MBean
提供的数据,还可以作为JFR
的分析工具,展示其数据。JFR
的基本工作逻辑是开启一系列事件的录制,即使不考虑性能影响的优势,JFR
的数据质量通常也比其他通过代理或者通过MBean
获取的数据要高。
3. HotSpot
虚拟机插件及工具
HotSpot
虚拟机也含有很多插件和辅助工具:
Ideal Graph Visualizer
:可视化展示C2
即时编译器是如何将字节码转化为理想图,再转化为机器码的。Client Compiler Visualizer
:查看C1
即时编译器生成高级中间表示 ( $HIR$ ),转换成低级中间表示 ( $LIR$ ) 和做物理寄存器分配的过程。MakeDeps
:处理HotSpot
的编译依赖。Project Creator
:生成Visual Studio
的 $.project$ 文件。LogCompilation
:将 $-XX:+LogCompilation$ 输出整理成更易阅读的形式。HSDIS
:即时编译器的反汇编插件。
3.1 HSDIS
:JIT
生成代码反汇编
HSDIS
插件通过HotSpot
的 $-XX:+PrintAssembly$ 指令调用,将即时编译器动态生成的本地代码还原为汇编代码输出,并产生注释。可以在http://lafo.ssw.uni-linz.ac.at/hsdis/att/上下载已编译好的HSDIS
,如果为Product
版虚拟机,还需要设置 $-XX:+UnlockDiagnosticVMOptions$ 。
在代码不多的时候,还可以直接查看输出,但是在大量代码中,显然无法直接查看。这时候就需要JITWatch
的辅助。可以在Github上下载源码进行编译。JITWatch
是用于分析日志的可视化工具,因此要将日志输出。输出日志可使用以下参数:
-XX:+UnlockDiagnosticVMOptions
-XX:+TraceClassLoading
-XX:+LogCompilation
-XX:LogFile=/tmp/logfile.log
-XX:+PrintAssembly
-XX:+TraceClassLoading
日志会输出到 $/tmp/logfile.log$ 文件中,在JITWatch
上加载后,即可看到各种使用过的对象类型、方法,以及源代码、字节码和汇编代码了。