JVM(3):性能监控和故障处理

1. 基础故障处理工具

        基础故障处理工具是JDK自带的一些位于 $/bin$ 目录下的小工具。这些工具主要用于监视虚拟机运行状态和进行故障处理,根据软件可用性和授权的不同,可以分为三类:

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 jinfoJava配置信息工具

        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 jmapJava内存映像工具

        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 jstackJava堆栈跟踪工具

        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中提供了JCMDJHSDB两个集成式的多功能工具箱,不仅整合了所有基础工具提供的而功能,而且还对他们进行了优化。

基础工具 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 JConsoleJava监视与管理控制台

        JConsole ( $Java\ \ Monitoring\ \ and\ \ Management\ \ Console$ ) 是一款基于JMX ( $Java\ \ Management\ \ Extensions$ ) 的可视化监视、管理工具,主要通过JMXMBean ( $Managed\ \ Bean$ ) 对系统进行信息收集和参数动态调整。
        JConsole的启动很简单,通过 $/bin$ 目录下的 $jconsole.exe$ 即可启动,在启动后会自动扫描虚拟机进程,也可以通过远程连接对远程虚拟机进行监控。

2.3 VisualVM:多合一故障处理工具

        VisualVM ( $All-in-One\ \ Java\ \ Troubleshooting\ \ Tool$ ) 是功能最强大的运行监视和故障处理程序之一。除了常规的运行监视和故障处理之外,它还提供了性能分析 ( $Profiling$ ) 等功能。VisualVM基于NetBeans平台开发工具,支持插件扩展。在官网上下载包后,可以直接导入。当然,VisualVM也提供了自动安装功能,能够找到大部分插件。
        VisualVM支持生成和浏览堆转储快照。虽然也支持性能分析功能,但是因为对运行有较大影响,因此一般不在生产环境使用,或者改用更强大、影响更小的JMC完成。通过插件,VisualVM中可以导入BTraceBTrace可以在不中断程序的前提下加入调试代码。

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虚拟机也含有很多插件和辅助工具:

3.1 HSDISJIT生成代码反汇编

        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上加载后,即可看到各种使用过的对象类型、方法,以及源代码、字节码和汇编代码了。

JVM(3):性能监控和故障处理