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 线程状态,随后是从当前调用点向上的栈帧。配图是结构说明图,不是本机运行截图。

按状态和栈顶方法解读结果
不要把 RUNNABLE 直接等同于“正在消耗 CPU”,也不要把 WAITING 直接等同于“线程坏了”。先看状态,再看栈顶方法和是否被锁占用:
| 线索 | 通常表示 | 下一步 |
|---|---|---|
BLOCKED | 等待进入 synchronized 监视器 | 结合锁拥有者和重复栈定位竞争点 |
WAITING/TIMED_WAITING | 等待通知、条件、Future 或定时器 | 检查等待对象与唤醒路径,不要只看线程数量 |
RUNNABLE | 可运行,可能正在执行也可能卡在本地调用或 I/O | 连续采集两到三次并比较栈顶是否变化 |
例如多个业务线程都停在同一个锁入口,且 -l 显示同一拥有者,优先排查锁的持有范围;多个线程都停在网络读取,则应转向连接超时和下游响应,而不是盲目扩大线程池。

线程很多时落盘并保留边界
如果现场线程较多,或者需要把快照交给另一个值班同事,可以使用文件化命令:
# 以纯文本保存线程栈,文件名建议包含 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 的一次线程栈采集才能从“输出很多”变成可执行的诊断线索。
-
479 收藏
-
337 收藏
-
128 收藏
-
149 收藏
-
202 收藏
-
205 收藏
-
208 收藏
-
247 收藏
-
322 收藏
-
454 收藏
-
133 收藏
-
481 收藏
-
152 收藏
-
476 收藏
-
493 收藏
-
292 收藏
-
115 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习