记得 2014 年我在写大宗期货交易平台的时候,遇到了一些棘手的问题,可能是因为我的并发编程知识掌握的不够扎实,导致出现了内存泄漏的问题。
当时排查了好久,用的工具就是 JDK 自带的 jconsole,之前也没有过类似的性能监控经验,就导致在查找问题的时候非常痛苦,至今印象深刻。
那今天我们就从工具篇出发,来看看这些命令行工具的具体使用方法,以及如何排查问题。
JDK 性能监控工具
除了我们的老朋友 java 和 javac 命令,在 Java 的 bin 目录下,还有很多其他的命令行工具,比如说用于性能监控的 jps、jstat、jinfo、jmap、jstack、jcmd 等等。
我用的 macOS,Windows 用户看到的可能是带有 .exe 结尾的,但是功能都是一样的,我就不再刻意去截图了。
接下来,我来给大家一一介绍一下这些工具的用途,认个脸熟。
jps:查看虚拟机进程
jps(Java Virtual Machine Process Status Tool)类似 Linux 下的 ps,用于快速查看哪些 Java 应用正在运行,以及它们的进程 ID,这对于进一步使用其他 JVM 工具进行诊断是必要的。
jps 命令格式:
jps [ options ] [ hostid ]
jps 命令示例:
①、注意看第三个进程正是我本地运行着的技术派实战项目,一个前后端分离的 Spring Boot+React 的社区项目,帮助不少球友拿到了心仪的校招 Offer。
②、pid 是什么?pid 是进程 ID,是操作系统分配给进程的唯一标识符,可以用来查看进程的详细信息。
通常情况下,我们关闭一个进程可以通过右上角的 X 号来完成,但有了 pid,我们可以直接在命令行通过 kill 命令来关闭进程,比如:
kill -9 pid
意思是强制关闭 pid 对应的进程,新手可千万别在生产环境下乱 kill 哈(😂)。
再来看一下 jps 的常用选项:
选项列表 | 描述 |
---|---|
-q | 只输出进程 ID,忽略主类信息 |
-l | 输出主类全名,或者执行 JAR 包则输出路径 |
-m | 输出虚拟机进程启动时传递给主类 main() 方法的参数 |
-v | 输出虚拟机进程启动时的 JVM 参数 |
jstat:查看 JVM 运行时信息
jstat(Java Virtual Machine Statistics Monitoring Tool)用于监控 JVM 的各种运行时状态信息,提供有关垃圾回收、类加载、JIT 编译等运行数据。
jstat 命令格式为:
jstat [ option vmid [interval[s|ms] [count]] ]
选项 option 主要分为三类:类加载、垃圾收集、运行期编译状况。
①、-class
:监视类装载、卸载数量、总空间以及类装载所耗费的时间。
如下命令 jstat -class -t 75952 1000 2
会输出进程 75952 的类装载信息,每秒统计一次,一共输出两次。
- Loaded:加载的类的数量。
- Bytes:所有加载类占用的空间大小。
- Unloaded:卸载的类的数量。
- Time:类加载器所花费的时间。
②、-gc
:监视 Java 堆状况,包括 Eden 区、2 个 Survivor 区、老年代等容量、已用空间、GC 时间合计等信息。
如下命令 jstat -gc 75952 1000 2
会输出进程 75952 的 GC 信息,每秒统计一次,一共输出两次。结果比较多,我就截断折叠了一下,方便大家查看。
- S0C, S1C, S0U, S1U:Survivor 区的大小和使用情况,一个 From 一个 To,C 为当前大小(Current),U 为已使用大小(Used)。
- EC, EU:Eden 区的大小和使用情况。
- OC, OU:老年代(Old)的大小和使用情况。
- MC, MU:元空间(Metaspace)的大小和使用情况。
- GC,GCC:GC 表示垃圾回收器进行 Minor GC(年轻代垃圾回收)的累计次数和总时间;GCC 表示垃圾回收器进行 Major GC(老年代垃圾回收,也称为 Full GC)的累计次数和总时间。
③、-compiler
:监视 JIT 编译器编译过的方法、耗时等信息。
- Compiled:编译的方法数量。
- Failed:编译失败的方法数量。
- Invalid:失效的编译方法数量。
- Time:编译所花费的时间。
如下命令 jstat -compiler 75952 1000 2
会输出进程 75952 的编译信息,每秒统计一次,一共输出两次。
好,我们再来总结一下 jstat 的主要选项,见下表:
选项列表 | 描述 |
---|---|
-class | 监视类加载、卸载数量、总空间以及类装载所耗费时长 |
-gc | 监视 Java 堆情况,包括 Eden 区、2 个 Survivor 区、老年代、元空间等,容量、已用空间、垃圾收集时间合计等信息 |
-gccapacity | 监视内容与-gc 基本一致,但输出主要关注 Java 堆各个区域使用到的最大、最小空间 |
-gcutil | 监视内容与-gc 基本相同,但输出主要关注已使用空间占总空间的百分比 |
-gccause | 与 -gcutil 功能一样,但是会额外输出导致上一次垃圾收集产生的原因 |
-gcnew | 监视新生代垃圾收集情况 |
-gcnewcapacity | 监视内容与 -gcnew 基本相同,输出主要关注使用到的最大、最小空间 |
-gcold | 监视老年代垃圾收集情况 |
-gcoldcapacity | 监视内容与 -gcold 基本相同,输出主要关注使用到的最大、最小空间 |
-compiler | 输出即时编译器编译过的方法、耗时等信息 |
-printcompilation | 输出已经被即时编译的方法 |
jinfo:查看虚拟机配置
jinfo(Configuration Info for Java)用于在补重启应用的情况下,调整虚拟机的各项参数,或者输出 Java 进程的详细信息。
jinfo 命令格式:
jinfo [ option ] pid
如下命令 jinfo -flags 88952
会输出进程 88952 的 JVM 参数信息。
不过很遗憾的是,我的 macOS 系统上,jinfo 命令无法执行成功,后来经过各种实验找到了解决办法。
可能的原因是,我的 macOS 上装了太多的 JDK 版本,导致 Intellij IDEA 中编译的 JDK 和 jinfo 的版本不一致。
那怎么解决呢?
尝试方案 1:用相同的 JDK 版本编译运行 Java 程序,并使用相同的 JDK 的 jinfo 来查看。
结果依然报错,可能的原因是 JDK 版本过旧。
回复