最高效的组合是jps -l → jstack -l → 直接定位末尾的死锁区块:第一步用jps -l精准识别目标java进程pid,第二步用jstack -l生成含锁详情的线程快照,第三步直接查看文件末尾“found one java-level deadlock”自动诊断结果,三步完成死锁确认。

最高效的组合是 jps -l → jstack -l
第一步:用 jps -l 快速锁定目标进程
不加参数的 jps 输出信息太简略,容易混淆;-l 参数显示完整类名,能准确区分你的应用和监控工具(比如 jconsole、jps 自身):
-
jps -l输出示例:12345 com.example.order.OrderService67890 sun.tools.jps.Jps - 只取你服务主类对应的 PID,比如
12345 - 在容器中执行时,若权限不足,需用应用运行用户(如
su - appuser -c "jps -l")
第二步:用 jstack -l 一次性抓全锁关系
必须带 -l 参数——它让 jstack 显示每个线程“持有哪些锁”和“正在等哪把锁”,这是识别循环等待的关键:
- 命令:
jstack -l 12345 > deadlock.log - 即使程序已卡死,该命令仍可成功执行(读的是 JVM 内存快照,不依赖业务逻辑)
- 输出文件里,所有锁对象都带 identity hashcode(如
),后续可直接在代码中搜索对应锁实例
第三步:直奔文件末尾,看 jstack 自动诊断结果
jstack 不仅打印堆栈,还会主动扫描并高亮死锁。打开 deadlock.log,拉到最底部找这个区块:
- 标题一定是:
Found one Java-level deadlock: - 下面清晰列出参与线程、谁在等谁:
"Thread-1": waiting to lock , which is held by "Thread-0""Thread-0": waiting to lock , which is held by "Thread-1" - 紧随其后的
Java stack information段会指出每行阻塞源码位置,例如:at com.example.service.PaymentService.transfer(PaymentService.java:42)
为什么这个组合最高效
它绕开了图形界面启动开销、避免了代码侵入、不需要提前埋点,且所有操作均可在生产环境低干扰执行:
- 全程命令行,无需 GUI 环境(适合服务器、容器、K8s Pod)
- 单次执行即可获得完整锁依赖链,不用反复采样
- 输出格式标准化,可配合 grep/awk 快速提取关键字段(如:
grep -A 10 "Found.*deadlock" deadlock.log) - 比 JConsole 更轻量,比 ThreadMXBean 更即时——后者需改代码、编译、重启
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











