jstack 线程 Dump 分析实战
更新时间:2026-09-18。本篇是操作级手册;线程状态语义与机制见 jstack 线程栈与 Java 线程状态。
一、采集
bash
jps # 1. 找到 Java PID
jstack -l <pid> > /tmp/t1.txt # 2. 连采 3 次,间隔 5~10 秒
jstack -l <pid> > /tmp/t2.txt
jstack -l <pid> > /tmp/t3.txt
jcmd <pid> Thread.print > /tmp/t.txt # 等价方式(JDK8+)容器环境需在目标容器内执行;若进程无响应可加 -F(强制,但信息可能不全,优先不用)。
二、场景一:CPU 100%
bash
top -Hp <pid> # 找 CPU% 最高的线程,记下十进制 tid
printf '%x\n' 12345 # 12345 -> 3039(十六进制)
grep -n "nid=0x3039" t1.txt # 在 Dump 中定位该线程栈- 栈顶在业务方法:死循环、复杂正则、序列化、加解密 → 优化代码。
- 栈顶是
GC 线程(VM Thread/GC task):转查 GC,见 jstat 与 Full GC 频繁。 - 栈顶
socketRead0/socketWrite0:实为阻塞 IO 等待,不是吃 CPU。
三、场景二:接口卡死/超时
对比三次 Dump,重点看业务 HTTP/RPC 线程(如 http-nio-*、Dubbo 线程):
- 大量线程
BLOCKED且waiting to lock <0x...>,同一把锁 → 锁竞争或某线程持锁不释放,找持锁线程栈。 - 大量线程
WAITING (parking)在连接池/下游调用 → 下游慢、连接池耗尽,结合 服务雪崩 看是否要加超时/熔断。 - 线程池空闲线程的 WAITING 属正常,别误报。
四、场景三:死锁
直接看 Dump 末尾:
bash
Found one Java-level deadlock:
"Thread-1": waiting to lock monitor 0x... (object 0x111),
which is held by "Thread-2"
"Thread-2": waiting to lock monitor 0x... (object 0x222),
which is held by "Thread-1"按报告的持锁/等锁关系,统一加锁顺序即可消除。
五、分析技巧与坑
- 务必连采多次:单次快照可能恰好经过,多次停在同一行才是真卡点。
- 给线程池命名:自定义
ThreadFactory(order-pool-%d),否则pool-N-thread-M无法归因业务。 -l不能省:才输出Locked ownable synchronizers,排查ReentrantLock(AQS)死锁必需。- 看 daemon 与优先级辅助判断:GC/编译线程是守护线程,别和业务线程混淆。
- 可用脚本/工具提效:
fastthread.io、tda等可视化;批量对比也可 grep 栈顶方法统计出现频率。
总结
jstack 排查三步走:jps 找进程、jstack -l 连采三次;CPU 高用 top -Hp 找最耗线程、tid 转十六进制 nid 回 Dump 看栈顶(区分业务计算、GC 线程和 socketRead 假性 RUNNABLE);接口卡死对比多次快照找集体 BLOCKED 的锁或集体 WAITING 的下游慢调用;死锁直接看 Dump 末尾 deadlock 段并统一加锁顺序。关键纪律是多次采样、线程池命名、带 -l 以便排查 AQS 锁,并在容器内采集。