jstack -l 是线上定位死锁最轻量可靠手段——无需改代码、不重启、不依赖监控;通过搜索“found one java-level deadlock”或比对locked/waiting to lock地址闭环可精准识别死锁,并结合堆栈回溯到源码行。

直接用 jstack -l <pid></pid> 抓取带锁信息的线程快照,是线上定位死锁最轻量、最可靠的一线手段——不需要改代码、不重启服务、不依赖监控系统,只要进程还在响应,就能拿到真实现场。
快速确认目标进程并抓取快照
先确保你操作的是正确的 Java 进程:
- 用
jps -l列出所有 Java 进程及其主类,或用ps -ef | grep java查看完整启动命令,注意区分多个 JVM 实例 - 确认进程启动用户,
jstack必须由同一用户执行;若权限不符,用sudo -u [user] jstack -l <pid></pid> - 立即执行:
jstack -l <pid> > thread_dump_$(date +%s).log 2>&1</pid>,-l 参数必不可少,它触发 JVM 自检并输出锁持有/等待关系 - 若进程已卡但未完全挂死,
jstack仍大概率成功;如提示 “unable to get thread dump”,可尝试kill -3 <pid></pid>触发堆栈输出到应用 stdout 或 gc.log(需提前配置日志重定向)
三秒识别死锁是否存在
打开日志文件,优先查找结构化线索:
- 直接搜索 “Found one Java-level deadlock:” —— 这是 JVM 自动检测到的经典循环等待,下面会明确列出互相等待的线程链和锁地址
- 没看到该提示?不代表没死锁。用
grep -A 5 -B 5 "waiting to lock\|locked"筛出关键行,重点看每个线程末尾的java.lang.Thread.State: BLOCKED (on object monitor)及其紧邻的locked和waiting to lock - 人工比对:若线程 A 持有
0x123等待0x456,线程 B 持有0x456等待0x123,即构成闭环,就是死锁
从堆栈回溯到具体代码行
锁定问题线程后,要精准落到源码位置:
- 提取锁地址(如
),在同份日志中全局搜索该地址,找到哪个线程locked它、在哪一行堆栈 ——at com.example.Service.process(Service.java:42)就是加锁点 - 区分锁类型:synchronized 锁的是对象监视器,地址对应对象实例;ReentrantLock 则显示
- parking to wait for,其锁对象是 AQS Node,需顺藤摸瓜找new ReentrantLock()初始化位置 - 若栈顶出现
Unsafe.park或LockSupport.park,说明已进入 AQS 队列,此时往前看几行业务方法调用链,结合请求 ID 或入参,反推触发路径
避免误判与增强判断可信度
单次快照可能捕捉瞬时阻塞,需交叉验证:
- 连续执行 2–3 次
jstack -l <pid></pid>,比对相同线程是否持续处于BLOCKED状态、持有的锁和等待的锁是否完全一致 - 若
jstack -l明确返回 “no deadlocks”,但程序卡住,可能是 I/O 阻塞、JUC 非公平锁竞争、Native 层死锁或长时间wait(),需结合jstack全量输出筛选长期停留某行的线程 - 配合
jstat -gc -t <pid> 1000 3</pid>排除 GC 异常干扰,确认卡顿真由锁竞争引起
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











