几个必会的JDK性能监控和故障处理工具

虽然阿里巴巴的 Arthas 这个工具很强大,不过如果你能够熟练使用 JDK 提供的工具,那么在不借助任何外力的情况下,就可以非常轻松的进行应用的性能监控和故障处理。

JDK 提供的工具,大家最熟悉的估计就是 java 和 javac 这两个了,然而 JDK 实际上提供了非常强大的性能监控和调试工具,今天松哥就来和大家一起捋一捋这些工具。

一 jps

这个应该是大家最为熟悉的工具了,使用这个命令可以快速列出来系统上正在运行的 Java 进程。

这个命令在执行的时候一般有四个参数:

  • -q 表示只输出进程 ID,不输出主类名称,准确来说,这里输出的叫做本地虚拟机进程 ID(LVMID),不过对于本地虚拟机进程来说,LVMID 和操作系统的进程 ID 是一致的。
  • -l 表示输出主类的全名,如果进程执行的是 jar 包,那么就输出 jar 包的完整路径。
  • -m 表示输出进程启动时传递给 main() 函数的参数。
  • -v 表示输出进程启动时的 JVM 参数。

二 jstat

jstat 是用来监控虚拟机各种运行状态信息的命令行工具,这个工具可以用来查看本地或者远程虚拟机内存、垃圾收集、即时编译等等信息。

一般来说,这些数据我们可以借助可视化工具来查看,例如 JMC 或者 VisualVM 等等,但是有时候在服务器环境下,在命令行环境下没法运行 GUI 图形界面,那么此时 jstat 是一个不错的选择。

jstat 运行参数比较多,松哥这里列举几个比较常见的参数:

  • -class 这个表示监视类加载、卸载数量、总空间以及类装载所耗费的时间。

  • -gc 这个表示监视 Java 堆状况,包括 Eden 区,两个 Survivor 区、老年代以及永久带的容量,已使用的空间以及垃圾收集时间合计等等信息。

命令中的 1s 8 表示每秒打印一次,一共打印 8 次,如果不设置,则打印一次。

这里输出的各参数含义如下:

S0C:Survivor0 的容量。
S1C:Survivor1 的容量。
S0U:Survivor0 已使用大小。
S1U:Survivor1 已使用大小。
EC:Eden 区容量。
EU:Eden 区已使用大小。
OC:老年代容量。
OU:老年代已使用大小。
MC:元数据区容量。
MU:元数据区已使用大小。
CCSC:压缩类空间容量。
CCSU:压缩类空间已使用大小。
YGC:年轻代垃圾回收次数。
YGCT:年轻代垃圾回收总耗时。
FGC:FullGC 次数。
FGCT:FullGC 总耗时。
CGC:GC STW 的次数。
CGCT:GC STW 的时间。
GCT:总 GC 时间。

基本上我们想知道的关于 JVM 运行内存状态的信息,这里都有了。

  • -gccapacity 这个监控内容与 -gc 类似,不过输出内容主要关注 Java 堆各个区域使用到的最大最小空间。

这里涉及到的参数我也和大家稍微解释下:

NGCMN:新生代最小容量。
NGCMX:新生代最大容量。
NGC:新生代当前容量。
OGCMN:老年代最小容量。
OGCMX:老年代最大容量。
OGC:老年代当前容量。
MCMN:元空间最小容量。
MCMX:元空间最大容量。
CCSMN:压缩类空间最小容量。
CCSMX:压缩类空间最大容量。
CCSC:压缩类空间当前容量。

补充输出内容和前面一致的我就不重复解释了。

  • -gcutil 这个和 -gc 类似,不过输出的内容主要关注已使用的空间占总空间的比例。

S0:Survivor0 区使用比例。
S1:Survivor1 区使用比例。
E:Eden 区使用比例。
O:老年代使用比例。
M:元空间使用比例。
CCS:压缩类空间使用比例。

这块能加的参数实际上非常多,对于我们来说比较常用的主要就是上面这几个命令。

如果小伙伴们想要查看完整指令,可以参考官方文档:https://docs.oracle.com/en/java/javase/11/tools/jstat.html

三 jinfo

jinfo 命令可以实时查看和修改 JVM 各项参数。

能修改这就很牛了。

平时我们启动一个 Java 项目,一旦启动之后,如果想要知道项目启动时的各项 JVM 参数,那么就只能去查文档了。但是如果你能够熟练使用 jinfo,那么这个事情就容易很多了。

直接 jinfo pid 就会展示出来所有的 JVM 参数。

如果只想查看某一个 JVM 参数,那么可以使用如下命令:

jinfo -flag CICompilerCount 28893

也可以在不重启 JVM 参数的情况下,动态修改 JVM 参数。

例如松哥这里的案例,原本是没有开启 PrintClassHistogram 的,我现在动态开启:

PrintClassHistogram 前面没有符号,表示查看当前状态;PrintClassHistogram 前面有个 + 表示开启;PrintClassHistogram 前面有个 - 表示关闭。

这种方式是修改 value 为 boolean 的 JVM 参数,如果是 key-value 格式的 JVM 参数,那么可以按照下面这种方式修改:

当然这个修改并非所有的 JVM 参数都能改,至于哪些 JVM 参数能改,我们可以执行 java -XX:+PrintFlagsInitial 命令,找到标记为 manageable 的参数,这些参数是可以动态修改的。

另外我们在代码中通过 System.getProperties() 获取到的属性,也可以通过指令打印出来:

jinfo -sysprops 57002

四 jmap

jmap 命令一般可以用来生成 heapdump。

这个命令在新版本 JDK 中运行的时候,前面要加上 jhsdb。

从这里我们可以解读出哪些信息呢?

  • 可以看到最大可分配的 Heap 大小(MaxHeapSize)
  • 可以看到 JVM 启动时分配的新生代内存(NewSize)
  • 可以看到可分配的最大新生代内存(MaxNewSize)
  • 可以看到 JVM 启动时分配的老年代大小(OldSize)
  • 可以看到老年代和新生代的内存比例(NewRatio),上面截图中表示 老/新=2,即老年代占 2/3,新生代占 1/3。
  • 可以看到新生代中 Eden 区和一个 Survivor 区的比例(SurvivoRatio)
  • 可以看到初始的 Metaspace 大小(MetaspaceSize)
  • 可以看到最大的 Metaspace 大小(MaxMetaspaceSize)
  • 可以看到在 G1 收集器下,每个 Region 的大小(G1HeapRegionSize)

下面的这些就好理解了,分别是不同区域的 regions 数量、总空间、使用空间以及空闲空间。

也可以利用 jmap 查看 Heap 中对象的统计信息,比如对象的数量、占用内存的大小以及类的完整路径。

jmap -histo:live pid

加上 :live 表示查看活着的对象信息。

也可以查看 Heap 中元空间的类加载信息 jmap -clstats 28893

这里可以看到有哪些类加载器,分别加载了哪些类。

当然,最重要的功能是生成 dump 文件了:

这里几个参数说明一下:

  • live 表示只转存堆中活动对象,如果没有制定,则存所有对象。
  • format=b 表示以 hprof 二进制格式转存。
  • file=xxx 表示生成的文件名。

生成的文件,可以使用 idea 打开查看。

小技巧:获取 heapdump 还有哪些方式?

  1. 使用 kill -3 吓一吓 JVM,就能拿到堆转储快照。
  2. -XX:HeapDumpOnOutOfMemoryError 参数可以让虚拟机在内存溢出时自动生成堆转储快照。

五 jhat

这个工具目前已经废弃了。

松哥这里就简单说下这个工具的作用以及替代品。

前面 jmap 会生成 heapdump 文件,这是一个二进制文件,那么我们通过什么工具去分析这个文件呢?比如可以使用 Eclipse 的 MAT,当然现在 IDEA 也可以直接打开这个文件分析,这块成熟的工具还是非常多的。

那么 jhat 其实也是一个这样的工具,jhat 内置了一个微型的 Web 服务器,让开发者可以在浏览器中查看相关数据。但是因为 jhat 功能比较简陋,所以目前已经废弃了。

六 jstack

jstack 可以查看 JVM 当前时刻的线程快照。

如果某个线程执行比较耗时,或者发生死锁,发生死循环等,那么就可以通过这个指令来查看各个线程的调用堆栈,检查到底发生了什么问题。

-l 表示出了堆栈之外,也把锁的相关信息显示出来。

不过这个命令松哥并不常用。因为 Thread 中有一个 getAllStackTraces 方法,我们通过 jstack 命令获取到的信息基本上通过这个方法都能获取到,所以可以自己结合这个方法写监控页面查看相关信息。

前面这个命令都是命令行工具,除了这些命令行工具之外,也有一些可视化工具,这个咱们下篇文章继续。

说到 JDK 工具,就不得不提这本 Java 虚拟机大作了《深入理解 Java 虚拟机》。

这本书的作者周志明博士是资深 Java 技术专家,对虚拟机技术有深入研究,这本《深入理解 Java 虚拟机》累计印刷超过 40 次,销量超过 30 万册,成为原创计算机专业图书领域的一个里程碑。

这本书将 Java 虚拟机(JVM)剖析得非常透彻,对于理解 JVM 的内存管理、垃圾收集、性能监控与故障排除等方面提供了清晰的指导。它不仅适合于面试准备,也能帮助解决工作中的实际问题。书中不仅讲解了理论知识,还通过大量实际案例分析,展示了如何解决各种 Java 技术难题。

全书一共分为五个部分,详细介绍了 Java 技术体系的发展、内存管理机制、虚拟机执行子系统、程序编译优化以及 Java 虚拟机对并发的支持。每一部分都与实际开发紧密相关,有助于读者将理论应用于实践。

无论是初学者还是有经验的开发者,都能从这本书中获得新的感悟和深入的理解。对于想要深入了解 JVM 的 Java 程序员来说,这本书被认为是“圣书”。

今天得到机械工业出版社的赞助,我们一共送出 5 本《深入理解 Java 虚拟机》,小伙伴们随便留言,我会选出来 5 位幸运小伙伴,这本书包邮到家!