登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  java教程

Java 怎样用 jcmd 查看线程栈

来源:17golang原创

时间:2026-10-18 14:02:28 339浏览 收藏

Java 服务出现请求变慢、线程数异常或疑似死锁时,先不要只看监控里的线程总数。使用 JDK 自带的 jcmd 可以直接向目标 JVM 请求线程栈:先用 jcmd -l 找到 PID,再执行 jcmd PID Thread.print -l。其中 -l 会补充可拥有锁信息,若需要更完整的线程属性,再加上 -e。

官方文档:https://docs.oracle.com/en/java/javase/25/docs/specs/man/jcmd.html

要点速览
  • jcmd -l 只负责发现当前主机上可见的 Java 进程,不代表每个 JVM 都能被附着。
  • Thread.print 输出所有线程和栈轨迹;-l 增加锁信息,-e 增加扩展线程信息。
  • 判断卡顿要结合状态、栈顶方法和重复堆栈;一次快照只能说明采集时刻的现象。

先确认 PID 和 jcmd 的附着条件

命令必须在 JVM 所在主机执行,并且当前用户与启动 JVM 的有效用户、组标识一致。最小发现命令如下:

# 列出当前主机上可见的 Java 进程、主类和启动参数
jcmd -l

# 也可以显式查看命令帮助,确认当前 JDK 的参数写法
jcmd -h

拿到 PID 后先不要直接套用网上的参数,先让目标 JVM 告诉你支持什么:

# 将 12345 替换成目标 Java 进程的 PID
jcmd 12345 help Thread.print

如果进程位于独立 Docker 进程中,jcmd -l 可能不会列出它,需要用容器内的 ps 或编排平台信息确认 PID。PID 找错时,后面的线程栈再完整也没有意义。

用 Thread.print 生成线程栈

最常用的命令是:

# 输出所有线程及其栈轨迹,并补充 java.util.concurrent 锁信息
jcmd 12345 Thread.print -l

# 需要扩展线程属性时再加 -e,避免把不必要的字段带入日常采集
jcmd 12345 Thread.print -e -l

官方命令定义里,Thread.print 的影响等级为 Medium,代价取决于线程数量;-l 打印 java.util.concurrent 锁,-e 打印扩展线程信息。输出中的每个线程通常包含线程名、优先级与 JVM 线程状态,随后是从当前调用点向上的栈帧。配图是结构说明图,不是本机运行截图。

Java jcmd Thread.print 从 PID 到线程栈和锁信息的结构说明图
图1:jcmd Thread.print 的命令、参数与线程栈输出关系说明图。

按状态和栈顶方法解读结果

不要把 RUNNABLE 直接等同于“正在消耗 CPU”,也不要把 WAITING 直接等同于“线程坏了”。先看状态,再看栈顶方法和是否被锁占用:

线索通常表示下一步
BLOCKED等待进入 synchronized 监视器结合锁拥有者和重复栈定位竞争点
WAITING/TIMED_WAITING等待通知、条件、Future 或定时器检查等待对象与唤醒路径,不要只看线程数量
RUNNABLE可运行,可能正在执行也可能卡在本地调用或 I/O连续采集两到三次并比较栈顶是否变化

例如多个业务线程都停在同一个锁入口,且 -l 显示同一拥有者,优先排查锁的持有范围;多个线程都停在网络读取,则应转向连接超时和下游响应,而不是盲目扩大线程池。

Java 线程栈状态 BLOCKED WAITING RUNNABLE 与排查方向的关系说明图
图2:从线程状态、栈顶方法和锁关系判断排查方向的静态说明图。

线程很多时落盘并保留边界

如果现场线程较多,或者需要把快照交给另一个值班同事,可以使用文件化命令:

# 以纯文本保存线程栈,文件名建议包含 PID 和采集时间
jcmd 12345 Thread.dump_to_file -format=plain /tmp/java-12345-threads.txt

# 需要程序化分析时输出 JSON;文件已存在时显式允许覆盖
jcmd 12345 Thread.dump_to_file -format=json -overwrite /tmp/java-12345-threads.json

落盘前确认目录权限和剩余空间。线程栈是快照,不是完整的时间线:一次采集只能说明这一刻在哪里等待。对疑似死锁或间歇性卡顿,间隔一小段时间重复采集,并比较相同线程的栈顶、锁拥有者和状态变化,结论才更可靠。

常见问题

jcmd 找不到目标 Java 进程怎么办?

先确认命令在目标 JVM 所在主机执行,再确认当前用户、容器边界和 PID。容器场景下从容器内部查看进程通常更直接。

Thread.print 和 Thread.dump_to_file 怎么选?

临时排查用 Thread.print 直接看输出;需要归档、跨机器传递或后续程序分析时,用 Thread.dump_to_file 保存 plain 或 JSON。

一次看到 RUNNABLE 就能判定 CPU 飙高吗?

不能。RUNNABLE 只说明线程处于可运行状态,仍需结合连续快照、CPU 采样和栈顶方法判断。

把 PID、参数、线程状态、栈顶方法和锁关系一起记录,jcmd 的一次线程栈采集才能从“输出很多”变成可执行的诊断线索。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>