Java JVM 分析工具


在 $JAVA_HOME/bin 的目录下, 存在着许多小工具, 除了编译和运行 Java 程序外, 打包, 部署, 签名, 调试, 监控, 运维等各种场景都可能会用到它们。

1 常用的命令行工具

1.1 jps (JVM Process Status Tool) - 虚拟机进程状况工具

列出正在运行的虚拟机进程, 并显示虚拟机执行主类 (Main Class, 也就是 main 方法所在的类) 的名称以及这些进程的本地虚拟机唯一 ID (LVMID,Local Virtual Machine Identifier)。

命令格式

jps [options] [<hostid>]

options 列表

选项 作用
-q 只输出 LVMID, 省略主类的名称
-m 输出虚拟机进程启动时, 传递给 main 方法的参数
-l 输出主类的全名称, 如果进程执行的是 jar 包, 输出 jar 包路径
-v 输出 JVM 进程启动时的 JVM 参数
-V 输出 LVMID 和主类的类名, 不带任何参数的 jps, 默认就是这个格式

jps 还可以查询开启了 RMI 服务的远程虚拟机进程状态, 参数 hostId 为 RMI 注册表中注册的主机名。开启 RMI 的步骤, 可以看这里

1.2 jstat (JVM Statistics Monitoring Tool) - 虚拟机统计信息监视工具

用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类加载, 内存, 垃圾收集, 即时编译等运行时数据。

命令格式

jstat -<options> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

options 列表

选项 作用
-class 监视类加载/卸载数量, 总空间和类装载消耗时间
-gc 监视堆状况, 包括 Eden 区/ 2 个 Survivor 区/老年代/永久代 等容量, 已用空间, 垃圾收集时间合计等信息
-gccapacity 监视内容和 -gc 差不多, 但输出内容主要关注的是 Java 堆各个区域的使用到的最大, 最小空间
-gcutil 监视内容和 -gc 差不多, 但输出内容主要关注的是 Java 堆各个区域已使用空间占总空间的百分比
-gccause 和 -gcutil 功能一样, 但是会多输出导致上一次垃圾回收产生的原因
-gcnew 监视新生代垃圾回收情况
-gcnewcapacity 监视内容和 -gcnew 基本一样, 输出主要关注使用到的最大, 最小空间
-gcold 监视老年代垃圾回收情况
-gcoldcapacity 监视内容和 -gcold 基本一样, 输出主要关注使用到的最大, 最小空间
-gcpermcapacity 输出永久代使用到的最大, 最小空间
-compiler 输出即时编译器编译过的方法, 耗时等信息
-printcompilation 输出已经被即时编译的方法

-t: 用于在最前面输出一个时间戳
-h: 格式 -h数字, 需要搭配后面的循环打印使用, 比如 5, 每个标题头下面输出 5 次结果后, 再次输出标题头, 再 5 次结果
vmid: 如果是本地的话, 就是 LVMID, 如果是远程的服务器的话, 则是类似于这样的格式 [protocol:][//]lvmid[@hostname[:port]/servername]
interval: 间隔多少毫秒查询 1 次, 如果为 数字 + s, 则是多少秒查询 1 次
count: 循环查询的次数

例子:

jstat -gcutil 2764

S0     S1     E       O      M     CCS      YGC     YGCT     FGC    FGCT       GCT
0.00  29.95  22.24   0.01  94.34  82.22      1      0.011     0     0.000      0.011

查询结果表明: 这台服务器的新生代 Eden 区 (E, 表示Eden) 使用了 22.24% 的空间, 2 个 Survivor 区 (S0, S1表示 Survivor0 和 Survivor1) 里面都是空的, 分别为空的和占了 29.95% 的空间, 老年代 (O, 表示 Old) 和 元空间 (M, 表示 Metaspace) 则分别使用了 0.01% 和 94.34% 的空间。

程序运行以来共发生 Minor GC (YGC, 表示 Young GC) 1次, 总耗时0.011秒; 发生 Full GC (FGC, 表示 Full GC) 0 次, 总耗时 (FGCT,表示 Full GC Time) 为 0.472 秒; 所有 GC 总耗时 (GCT, 表示 GC Time) 为 0.577 秒

1.3 jinfo (Configuration Info for Java) - Java 配置信息工具

实时查看和调整虚拟机各项参数

命令格式

jinfo -<options> <pid>

options 列表

选项 作用
-flag name 输出指定的 JVM 参数的值
-flag +/- name 动态的启用/关闭指定的 JVM 参数
-flag name=value 动态的设置 JVM 参数为给定的值
-flags 输出所有的 JVM 参数
-sysprops 输出当前的 Java 系统属性, 内容等同与 Java 内部的 System.getProperties()

1.4 jmap (Memory Map for Java) - Java 内存映像工具

jmap 的作用并不仅仅是为了获取堆转储快照 (一般称为 heapdump 或 dump 文件), 它还可以查询 finalize 执行队列, Java 堆和方法区的详细信息, 如空间使用率, 当前用的是哪种收集器等

命令格式

jmap -<options> <pid>

options 列表

选项 作用
-dump 生成 Java 堆转储快照, 格式为 -dump:[live,]format=b,file=<文件名>, live 参数用于控制是否只 dump 出存活的对象
-finalizerinfo 显示在 F-Queue 中等待 Finalizer 线程执行 finalize 方法的对象, 只在 Linux/Solaris 平台有效
-heap 显示 Java 堆的详细信息, 如使用的是哪种收集器, 参数配置, 分代状况等, 只在 Linux/Solaris 平台有效
-histo 显示堆中对象统计信息, 包含类/实例数量, 合集容量, 格式为 -histo[:live,][file=<文件名>], live 参数用于控制统计活对象, 同样的也可以直接把内容输出为文件
-permstat 以 ClassLoader 为统计口径显示永久代内存状态, 只在 Linux/Solaris 平台有效
-F 当虚拟机对 -dump 选项没有任何反应, 可使用这个参数强制 dump 出快照, 只在 Linux/Solaris 平台有效

除了手动生成快照的方式, 也可以通过配置 JVM 参数 XX: +HeapDumpOnOutOfMemoryError, 当程序内存溢出时, 生成 dump 文件
也可以通过设置 -XX: +HeapDumpOnCtrlBreak 参数, 然后通过 [Ctrl]+[Break] 键让虚拟机生成堆转储快照文件
在 Linux 中也可以通过 “Kill -3” 来获取堆转储快照

1.5 jhat (JVM Heap Analysis Tool) - 虚拟机堆转储快照分析工具

jhat 内置了一个微型的HTTP/Web服务器, 可以用来分析 jmap 生成的 dump 文件, 同时生成堆转储快照的分析结果后。
但是实际中很少会使用这个来分析 dump 文件, 有很多更智能的图形化工具可以使用。

命令格式

jhat dump文件

通过上面的命令, 当显示 “Server is Ready” 的提示后, 通过输入 localhost:7000 就能查看分析信息。

1.6 jstack (Stack Trace for Java) - Java 堆栈跟踪工具

用于生成虚拟机当前时刻的线程快照 (一般称为 threaddump 或者 javacore 文件)。
线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合, 生成线程快照的 目的通常是定位线程出现长时间停顿的原因, 如线程间死锁, 死循环, 请求外部资源导致的长时间挂起等, 都是导致线程长时间停顿的常见原因。
线程出现停顿时通过 jstack 来查看各个线程的调用堆栈, 就可以获知没有响应的线程到底在后台做些什么事情, 或者等待着什么资源。

命令格式

jstack -<options> vmid

options 列表

选项 作用
-F 当正常的输出请求无响应时, 强制输出线程堆栈信息
-l 除堆栈信息外, 显示锁的附加信息
-m 如果调用到了本地方法的话, 输出 C/C++ 的堆栈

在 $JAVA_HOME/bin 下除了列出的这几个常用的工具外, 还有许多有用的工具, 如 javap 字节码分析工具, javac java 类文件转字节码文件等。

2 JVM 可视化工具

2.1 JHSDB - JDK 自带的工具

JHSDB 是一款基于服务性代理 (Serviceability Agent, SA) 实现的进程外调试工具。

服务性代理是 HotSpot 虚拟机中一组用于映射Java虚拟机运行信息的, 主要基于 Java 语言 (含少量 JNI 代码) 实现的 API 集合。
服务性代理以 HotSpot 内部的数据结构为参照物进行设计, 把这些 C++ 的数据抽象出 Java 模型对象, 相当于 HotSpot 的 C++ 代码的一个镜像。

通过服务性代理的 API, 可以在一个独立的 Java 虚拟机的进程里分析其他 HotSpot 虚拟机的内部数据, 或者从 HotSpot 虚拟机进程内存中 dump 出来的转储快照里还原出它的运行状态细节。

基于 JHSDB 的特点一般用于分析处于静止不变的程序, 比如卡在 debug 或者 dump 文件。

使用 JHSDB 的步骤

  1. 当前你有一个卡在 debug 的程序或者一份 dump 文件
  2. 启动 JHSDB, JDK8 中通过 java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB, 启动 JHSDB 的图形化界面, 通过
    java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB 启动 JHSDB 的命令行界面, JDK9 及以后可以转到 $JAVA_HOME/bin,
    执行 jhsdb hsdb (本人在 Mac 一直失败, 貌似 JDK8 在 Mac 上有 bug, 可以尝试升级 JDK 的版本, 在 Window10 成功关联上了)
  3. 获取当前执行的中的 Java 程序的进程 ID
  4. 打开图形化界面, 左上角的 File, 选择关联进程 ID / dump 文件 / 服务地址

后面就是分析相关的操作了

操作可以看这里

2.2 JConsole

JConsole (Java Monitoring and Management Console) 是一款基于 JMX (Java Management Extensions) 的可视化监视,管理工具。它的主要
功能是通过 JMX 的 MBean (Managed Bean) 对系统进行信息收集和参数动态调整。

在 $JAVA_HOME/bin 下面有一个 jconsole 的脚本, 直接执行它, 就会出现一个图形化界面, 在首页选中自己的进程, 就能进入分析。

2.3 VisualVM

VisualVM (All-in-One Java Troubleshooting Tool) 是功能最强大的运行监视和故障处理程序之一, 着它除了常规的运行监视, 故障处理外,还将提
供其他方面的能力, 譬如性能分析 (Profiling)。

在 $JAVA_HOME/bin 下面有一个 jvisualvm 的脚本, 直接执行它, 就会出现一个图形化界面, 在左侧的应用程序选中自己的程序即可。

VisualVM 可以做到:

  1. 显示虚拟机进程以及进程的配置, 环境信息 (jps, jinfo)
  2. 监视应用程序的处理器, 垃圾收集, 堆, 方法区以及线程的信息 (jstat, jstack)
  3. dump 以及分析堆转储快照 (jmap, jhat)
  4. 方法级的程序运行性能分析, 找出被调用最多, 运行时间最长的方法
  5. 离线程序快照: 收集程序的运行时配置, 线程 dump, 内存 dump 等信息建立一个快照, 可以将快照发送开发者处进行 Bug 反馈
  6. VisualVM 只提供了基础的功能, 同时支持大量的插件, 通过安装插件, 可以带来的无限可能性

2.4 商业版工具

Oracle 公司还开辟过带商业技术支持的工具

  1. 用于企业 JRE 定制管理的 AMC (Java Advanced Management Console) 控制台
  2. 系统跟踪的 JUT (Java Usage Tracker)
  3. 用于持续收集数据的飞行记录仪 JFR (Java Flight Recorder)
  4. 用于监控 Java 虚拟机的JMC (Java Mission Control)

3 第三方工具

通过 jmap 生成的 dump 文件, 除了可以通过 jhat 来分析外, 还可以通过其他的工具来分析, 如下面的这些工具

  1. MAT
  2. JProfiler
  3. 第三方的网站, 如 HeapDump

通过配置 JVM 启动参数生成的 GC 文件分析工具

  1. GCViewer
  2. 第三方的网站, 如 Ycrash GCeasy

  目录