生产环境黄金法则:当JVM出现异常时,命令行工具是比可视化界面更可靠的诊断手段。本文将深入解析JVM核心命令行工具,助你成为线上问题排查专家。
不同的命令有不同的使用场景,根据要分析的内容选择合适的命令工具,注意其中的开销对性能的影响。
| JVM工具命令 | 使用场景 | 生产适用性 | 执行开销 |
|---|---|---|---|
| jcmd | 综合诊断 | ⭐⭐⭐⭐⭐ | 极低 |
| jstack | 线程分析 | ⭐⭐⭐⭐ | 低 |
| jmap | 内存分析 | ⭐⭐⭐ | 中高 |
| jstat | GC监控 | ⭐⭐⭐⭐⭐ | 极低 |
| jinfo | 参数查看 | ⭐⭐⭐ | 低 |
jcmd 是 Oracle JDK(Java Development Kit)自 JDK 7 起引入的一个强大的诊断工具,用于与正在运行的 JVM(Java Virtual Machine)实例进行交互。它允许用户执行各种诊断命令,比如 线程堆栈分析、堆转储、GC 信息、类加载器统计 等,非常适合在生产环境中对 Java 应用程序进行调试和监控。
sh # 语法 jcmd <pid | main class> <command ...|PerfCounter.print|-f file>
<pid>: 目标 Java 进程的进程 ID。<main-class>: 目标 Java 应用的主类名(可以省略 jar 路径)。<command>: 具体的诊断命令,如 VM.version、Thread.print、GC.heap_info 等。[options]:命令附带的参数(具体取决于命令)。sh # 查看所有支持的命令 jcmd <pid> jcmd <pid> help
展示所有命令信息:

具体用法:
sh # 查看JDK版本 jcmd <pid> VM.version # 查看启动参数 jcmd <pid> VM.command_line # 强制触发 GC jcmd <pid> GC.run # 查看内存统计,显示堆占用情况(老年代、新生代等)。 jcmd <pid> GC.heap_info # 检查是否发生了死锁,如果存在死锁,会提示 Found one Java-level deadlock jcmd <pid> Thread.print # 生成线程快照(替代jstack),用于诊断死锁或高 CPU 问题。 jcmd <pid> Thread.print > thread_dump.txt # 生成堆转储文件(替代jmap),用于分析内存泄漏或对象分布。 jcmd <pid> GC.heap_dump ./dump.hprof # 获取类加载统计,显示堆中存活对象的数量及内存占用,按类排序。 jcmd <pid> GC.class_histogram
提示
查询java进程信息
sh jps -l
查找类加载统计,包含包路径的前10条记录
sh jcmd 7 GC.class_histogram | grep 'com.liushigong' | head -n 10
查询死锁信息
sh jcmd 7 Thread.print | grep 'Found one Java-level deadlock' -A 20
查询状态为阻塞的线程
sh jcmd 7 Thread.print | grep 'BLOCKED' -A 20
jstack 是 JDK 提供的一个命令行工具,用于生成 Java 虚拟机(JVM)的线程转储(Thread Dump).通过分析线程转储,可以快速定位死锁、线程阻塞、CPU 占用过高等问题。
线程转储是 JVM 中所有线程状态的快照,包含以下关键信息:
sh # 语法 jstack [options] <pid> jstack [options] <executable> <core> jstack [options] [server_id@] <remote server IP or hostname>
sh # 基础线程快照 jstack <pid> > thread_dump.txt # 包含锁信息(-l参数) jstack -l <pid> > locked_threads.txt # 追踪死锁(结合grep) jstack <pid> | grep -A 1 deadlock # 查找BLOCKED状态的线程,有助于快速定位可能存在线程阻塞或死锁的问题。 jstack -l [pid] | grep "BLOCKED" # 远程调试,需要指定远程服务器的IP或主机名。 jstack [server_id@] <remote server IP or hostname>
关键信息解读:
java "main" #1 prio=5 os_prio=0 tid=0x00007f8b4800e800 nid=0x1a03 waiting on condition [0x00007f8b50a0e000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076b8a1c98> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.example.MyService.doSomething(MyService.java:42) // 定位到源码行
适用场景:
注意
执行时 JVM 会有短暂停顿(Safepoint)
sh # 堆直方图(安全) jmap -histo:live <pid> > histo.txt # 堆转储(谨慎使用) jmap -dump:live,format=b,file=heap.hprof <pid> # 内存使用概况 jmap -heap <pid>
注意
使用 jmap 命令时需要注意以下几点:
-dump:live 会触发 Full GCjcmd <pid> GC.heap_dump ./dump.hprof适用场景:
jmap -dump:live,format=b,file=heapdump.hprof),但可以通过 jmap 生成整个堆的快照(尽管这通常需要暂停应用程序的运行)。这对于后续使用如 Eclipse Memory Analyzer (MAT) 等工具进行详细分析非常有用。生产环境安全操作
随着 JDK 的发展,一些新的工具如 jcmd(通过 jcmd <pid> VM.native_memory 和 jcmd <pid> GC.class_histogram、jcmd <pid> GC.heap_dump ./dump.hprof)提供了类似的功能,且不会暂停应用程序运行。
sh # 1. 设置OOM自动转储(避免手动执行) -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path # 2. 使用低开销替代方案 jcmd <pid> GC.class_histogram > histo.txt jcmd <pid> GC.heap_dump ./dump.hprof
jstat (Java Virtual Machine Statistics Monitoring Tool) 是用于监控和分析 Java 虚拟机 (JVM) 性能的工具。它提供了 JVM 的运行时信息,比如垃圾收集的状态、内存使用情况、类加载信息等。jstat 是 JDK 中自带的命令行工具,可以帮助开发者和运维人员排查和优化 JVM 性能问题。
sh # 语法 jstat [option] [vmid] [interval] [count]
sh # 每2秒打印GC情况(持续监控) jstat -gcutil <pid> 2000 # 每2秒打印GC原因 jstat -gccause <pid> 2000

关键指标解读:
适用场景:
查看当前 JVM 参数或动态修改部分参数(如日志级别)。
sh jinfo [option] <pid>
-flag <name>: 获取指定参数的值。-flag [+|-]<name>: 打开或关闭指定参数。-flag <name>=<value>: 设置指定参数的值。-flags: 打印所有JVM参数。-sysprops: 显示JVM的系统属性sh # 查看所有参数 jinfo -flags <pid> # 查看单个参数值 jinfo -flag MaxHeapSize <pid> # 动态修改参数(仅支持部分) jinfo -flag +HeapDumpOnOutOfMemoryError <pid>
查看这个JVM进程查看和修改虚拟机的参数,但是修改的有限制的,只有manageble的参数才可以动态修改。其他的必须重启的时候设置修改。
sh java -XX:+PrintFlagsFinal –version

适用场景:
注意
注意生产环境慎用写操作
bash # 1. 定位高CPU线程 top -H -p <pid> # 记录线程ID(十进制) # 2. 转换线程ID为十六进制 printf "%x\n" 11542 # -> 2d16 # 3. 抓取线程栈 jstack <pid> | grep -A 20 'nid=0x2d16' # 4. 分析栈轨迹(发现死循环) while (true) { calculate(); } // 定位问题代码
bash # 1. 监控GC趋势(发现Old区持续增长) jstat -gcutil <pid> 1000 10 # 2. 获取堆直方图 jcmd <pid> GC.class_histogram > histo_day1.txt # 3. 对比数据(8小时后再次抓取) diff histo_day1.txt histo_day2.txt # 4. 发现MyObject数量从1k增至5k # 5. 生成堆转储分析引用链
| 工具 | 优势 | 适用场景 |
|---|---|---|
| Eclipse MAT | 内存泄漏检测强大 | 复杂内存泄漏 |
| VisualVM | 直观易用 | 快速对象分析 |
| JDK Mission Control | 低开销采样 | 生产环境实时分析 |
泄漏嫌疑分析:
定位GC Roots:
java // 示例:ThreadLocal泄漏 Thread @ 0x7fe45b88d -> ThreadLocalMap @ 0x7fe4a3f1021 -> MyObject @ 0x7fe4a3f1045 (1000个实例)
以上简述了分析流程,我将写一篇专门的MAT工具实战分析。
bash # 1. 实时监控GC watch "jstat -gcutil <pid> 1000 1" # 2. 发现Old区增长后立即抓取 jcmd <pid> GC.heap_dump filename=leak.hprof # 3. 同时获取线程栈 jstack <pid> > thread_state.log # 4. 分析对象与线程关联 grep 'MyObject' thread_state.log -A 5
bash # 启动时预置诊断参数 java -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/logs/heapdumps \ -XX:ErrorFile=/logs/hs_err_pid%p.log \ -Xlog:gc*:file=/logs/gc.log
-XX:HeapDumpMaxFileSize=1gbash # 进入容器执行命令,查看服务的 pid docker exec -it <container> jcmd # 分析堆信息 docker exec -it <container> jcmd <pid> GC.heap_info # Kubernetes诊断Pod kubectl exec <pod> -- jstack 1 > thread.log
| 问题类型 | 推荐工具组合 |
|---|---|
| CPU 飙高 | top + jstack + Arthas trace |
| 接口卡顿 | Arthas tt/watch/trace |
| 内存泄漏 | jmap + MAT + jstat |
| 类加载冲突 | jad, sc, classloader |
| 容器内调试 | Arthas 支持 docker 内 attach |
线上性能问题,无非就2种,一种是CPU飙升,另一种就是内存打满,根据不同的问题类型使用不同的命令工具分析。90%的JVM问题可通过 jcmd+jstack+jstat 组合定位。
记住:优秀的开发者能写代码,卓越的工程师能救火!
本文作者:张豪
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!