「JVM 故障诊断」命令行工具

给定一个系统问题,知识、经验是关键基础,数据(异常堆栈、JVM 运行日志、GC 日志、线程快照 threaddump/javacore、堆转储快照 heap dump/hprof 等)是依据,工具是运用知识处理数据的手段;

  • 商业授权工具: 在商业环境中使用是要付费的;如 JMC(Java Mission Control)及 JFR(Java Flight Recorder);
  • 正式支持工具: 长期支持的工具,不会突然消失,移除前会先打上 Deprecated 过渡;
  • 实验性工具: Unsupported and Experimental,可能无声无息的消失,也可能转正;通常也是稳定且强大的工具;

1. jps(JVM Process Status Tool)

列出正在运行的 Java 进程,并显示虚拟机执行主类(Main Class,main() 函数所在的类)及进程的本地虚拟机唯一 ID(LVMID,LOcal Virtual Machine Identifier,与操作系统的进程 ID,PID 一致);

命令格式

# options,命令选项
#   -q: 仅展示 LVMID(local Virtual Machine ID)
#   -m: 显示主类 main() 的参数
#   -l: 显示主类的全类名,或 -jar 的 jarfile 完整路径
#   -v|V: 显示 JVM 参数
# ------
# hostid,可指定远程主机,通过 RMI 协议查看开启了 RMI 服务的远程 JVM 进程状态;jstatd 可以建立远程 RMI 服务器
jps [options] [hostid]

关闭 -XX:-UsePerfData 后,将无法通过 jps 命令探知 Java 进程;

执行示例

jps -l
73089 sun.tools.jps.Jps
15283 /Users/cm.huang/.vscode/extensions/redhat.java-1.14.0-darwin-arm64/server/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar
68393 org.jetbrains.jps.cmdline.Launcher
2250

2. jstat(JVM Statistics Monitoring Tool)

监视 JVM 各种运行状态信息(类加载、内存、垃圾收集、JIT 编译等运行时数据);

命令格式

# option,命令选项
#   -t: 显示打印时间
#   -h <lines>: 隔几行打印一次表头
# ------
#   -class: 监视类加载、卸载数量、总空间、类装载耗时
#   -gc: 监视 Java Heap 状态(Eden、2 个 Survivor、老年代、永久代等的容量、已用空间,以及 GC 时间合计等
#   -gccapacity: 与 -gc 相似,主要输出 Java Heap 各区的使用到的最大、最小空间
#   -gcutil: 与 -gc 相似,主要输出已使用空间站总容量的百分比
#   -gccause: 与 -gcutil 相似,会额外输出导致上次 GC 的原因
#   -gcnew: 监视新生代 GC 情况
#   -gcnewcapacity: 与 -gcnew 相似,主要输出使用到的最大、最小空间
#   -gcold: 监视老年代 GC 情况
#   -gcoldcapacity: 与 -gcold 相似,主要输出使用到的最大、最小空间
#   -gcpermcapacity: 输出永久代使用到的最大、最小空间
#   -compiler: 输出 JIT 编译过的方法、耗时等
#   -printcompilation: 输出 JIT 编译过的方法
# ------
# vmid,虚拟机进程号,本地 VM 则与 lvmid 一致,远程 VM 则满足格式: [protocol:][//]lvmid[@hostname[:port]/servername]
# interval,隔多少毫秒查询一次
# count,查询几次,如果不给定 interval 和 count 则只查询一次
jstat [ option vmid [interval[s|ms] [count]] ]

执行示例

# 查询 15283 进程的垃圾收集情况,每 250ms 查询一次,共查 5 次;
# ------
# S0C、S1C: Survivor 区容量
# S0U、S1U: Survivor 区使用量
# EC: Eden 区容量
# EU: Eden 区使用量
# OC: Old 区容量
# OU: Old 区使用量
# MC: 方法区容量
# MU: 方法区使用率
# CCSC: Compressed Class Space 容量
# CCSU: Compressed Class Space 使用量
# YGC: Young GC 次数
# YGCT: Young GC 总耗时
# FGC: Full GC 次数
# FGCT: Full GC 总耗时
# GCT: 所有 GC 总耗时
jstat -gc 15283 250 5
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
2048.0 4096.0 1952.0  0.0    8704.0   1786.8   88576.0    71827.4   55616.0 54818.9 6400.0 6044.0     25    0.101   3      0.064    0.166
2048.0 4096.0 1952.0  0.0    8704.0   1786.8   88576.0    71827.4   55616.0 54818.9 6400.0 6044.0     25    0.101   3      0.064    0.166
2048.0 4096.0 1952.0  0.0    8704.0   1786.8   88576.0    71827.4   55616.0 54818.9 6400.0 6044.0     25    0.101   3      0.064    0.166
2048.0 4096.0 1952.0  0.0    8704.0   1786.8   88576.0    71827.4   55616.0 54818.9 6400.0 6044.0     25    0.101   3      0.064    0.166
2048.0 4096.0 1952.0  0.0    8704.0   1786.8   88576.0    71827.4   55616.0 54818.9 6400.0 6044.0     25    0.101   3      0.064    0.166

3. jinfo(Configuration Info for Java)

实时查看和修改 JVM 参数的当前值;

命令格式

# option,命令选项
#   -sysprops: 查看 System.getProperties() 取得的参数
#   -flags: 查看所有 JVM 参数的当前值;与 -XX:+PrintFlagsInitial 参数作用相当
#   -flag name: 查看具体 JVM 参数的当前值
#   -flag [+|-]name: 修改 Boolean 类型参数
#   -flag name=value: 修改非 Boolean 类型参数
jinfo [ option ] pid

执行示例

jinfo -flag CMSInitiatingOccupancyFraction 1444
-XX:CMSInitiatingOccupancyFraction=85

# 打印 VM 参数的初始值
java -XX:+PrintFlagsInitial -version

4. jmap(Memory Map for java)

用于审查堆转储快照;还可以查询 finalize 执行队列、Java Heap 和 Method Area 的详细信息(使用率、使用的 GC 等);

命令格式

# option,命令选项
#   -dump: 导出 Java Heap 转储快照;格式为:-dump:[live,]format=b,file=<filename>;其中 live 子项表示是否只 dump 存活的对象
#   -finalizerinfo: 显示堆积在 F-Queue 队列中等待 Finalizer 线程执行 finalize() 的对象
#   -heap: 显示堆内存详细信息(使用的 GC、参数配置、分代状态等)
#   -histo: 显示堆中对象统计信息(类、实例数、合计容量)
#   -permstat/clstats: 以 ClassLoader 为口径统计方法区内存信息
#   -F: 当虚拟机进程对 -dump 没有响应时,可以使用该选项强制生成 dump
#   -h|help: 帮助文档
#   -J<flag>: 传入启动参数
jmap [ option ] vmid

# eg.
# jmap dump heap snapshot
jmap -dump:live,format=b,file=heap.hprof vmid

# -finalizerinfo 打印正在等候回收的对象信息
jmap -finalizerinfo vmid

# 打印 heap 概要信息
jmap -heap vmid

# 打印每个 class 实例数量
jmap -histo:live vmid

# 打印 classloader 统计信息
jmap -clstats vmid

执行示例

jmap -dump:format=b,file=gobrs.bin 23161
Dumping heap to /Users/cm.huang/gobrs.bin ...
Heap dump file created

其他强制 dump heap 的方式

# JVM 在内存溢出异常时自动生成 dump
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=filename.hprof

# 使用 Ctrl + Break 键让 JVM 生成 dump
-XX:+HeapDumpOnCtrlBreak

# Linux 下发送进程退出信号`恐吓` JVM 生成 dump
kill -3 pid

5. jhat(JVM Heap Analysis Tool)

与 jmap 搭配使用,用于分析 jmap 生成的 heap dump;

一般不会真的需要使用 jhat 进行堆快照分析;jhat 功能相对比较简陋,既然 heap dump 已经拿到,完全可以使用更专业和方便的可视化工具(Visual VM、Eclipse Memory Analyzer、IBM Heap Analyzer 等);

命令格式

# -stack: 打开调用栈跟踪
jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>

执行示例

# 启动一个端口为 7777 的本地微型 HTTP/Web 服务
jhat -port 7777 gobrs.bin

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAi5k8AS-1675177251771)(./images/jhat%20demo.png)]

页底的 Show heap histogram 类似 jmap 的 -histo 功能;OQL Query 则可以通过类似 SQL 的语法对内存中的对象进行统计查询;

6. jstack(Stack Trace for Java)

生成 JVM 当前时刻的线程快照(threaddump 或 javacore,当前 JVM 内每一条线程正在执行的方法堆栈的集合):

jstack 对于分析线程间死锁、死循环、请求外部资源长时间挂起等线程长时间停顿的问题有巨大帮助;

命令格式

# option,命令选项
#   -F: 正常输出的请求不被响应时,强制输出线程堆栈
#   -l: 除堆栈外,显示关于锁的附加信息
#   -m: 若调用了本地方法,可以显示 C/C++ 的堆栈
#   -h/help: 打印帮助信息
jstack [ option ] vmid

执行示例

# 查看 23161 进程的堆栈,并将结果记录到 thread.log 文件
jstack -l 23161 > thread.log

关于锁的描述

  • Deadlock: 死锁
  • Waiting on condition: 等待资源
  • Waiting on monitor entry: 等待获取监视器
  • Blocked: 阻塞
  • Runnalbe: 执行中
  • Suspended: 暂停
  • Object.wait() 或 TIMED_WAITING: 对象等待中
  • Parked: 停止

StackTraceElement

java.lang.Thread.getAllStackTraces() 可以得到一个 StackTraceElement 对象,可以通过该方法在代码中实现 jstack 的大部分功能;

for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet()) {
    Thread thread = (Thread) stackTrace.getKey();
    StackTraceElement[] stack = (StackTraceElement[]) stackTrace.getValue();
    if (thread.equals(Thread.currentThread())) {
        continue;
    }
    System.out.print("\n线程:" + thread.getName() + "\n");
    for (StackTraceElement element : stack) {
        System.out.print("\t" + element + "\n");
    }
}

7. jcmd(JVM Command)

实现除 jstack 外全部命令功能

命令格式

#  or: jcmd -l
#  or: jcmd -h

# command must be a valid jcmd command for the selected jvm.
# Use the command "help" to see which commands are available.
# If the pid is 0, commands will be sent to all Java processes.
# The main class argument will be used to match (either partially
# or fully) the class used to start Java.
# If no options are given, lists Java processes (same as -p).

# PerfCounter.print display the counters exposed by this process
# -f  read and execute commands from the file
# -l  list JVM processes on the local machine
# -h  this help
jcmd <pid | main class> <command ...|PerfCounter.print|-f file>

# eg.
# 查看 JVM 进程的 Native Memory Tracking 信息
jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]

# 列出所有 Java 进程
jcmd -l

# 列出针对指定进程的所有可用指令
jcmd pid help

# 针对指定进程执行指定命令
jcmd pid <cmd>

执行示例

jcmd -l
25364 sun.tools.jcmd.JCmd -l
23161 com.gobrs.async.example.GobrsAsyncExampleApplication
1916

8. jstatd(JVM Statistics Monitoring Tool Daemon)

jstat 的守护进程,启动一个 RMI 服务,用于监视 HotSpot VM,运行远程监控工具跨网络附加上来;


上一篇:「JVM 内存管理」内存分配与回收策略
下一篇:「JVM 故障诊断」可视化工具(WIP)

PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!


参考资料:

  • [1]《深入理解 Java 虚拟机》
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三余知行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值