锁持有变量链是jstack -l输出中体现的线程间锁依赖关系:某线程持有对象锁(locked ),另一线程等待同一锁(waiting to lock ),二者地址相同,构成可追溯的业务变量级锁传递链。

jstack -l 是排查 Java 应用死锁问题的关键命令,它不仅能输出线程堆栈,还能显示锁的持有关系与等待链,尤其在识别“锁持有变量链”(即谁持有了哪个对象锁、谁在等待该锁)时非常直观。
什么是锁持有变量链?
它不是 Java 语言层面的语法概念,而是 jstack -l 输出中体现的一种逻辑依赖关系:某个线程正在执行某段代码,持有一个 java.lang.Object 或 java.util.concurrent.locks.ReentrantLock 实例的锁;而另一个线程正阻塞在 Object.wait()、Object.wait(long)、Lock.lock() 等调用上,等待获取同一把锁。jstack -l 会明确标出 locked 和 waiting to lock ,这些十六进制地址指向堆中具体的锁对象——这就是变量链的锚点。
如何用 jstack -l 定位死锁中的变量链
执行以下命令(需有目标 JVM 进程权限):
jstack -l
然后在输出中重点关注两部分:
- 搜索 Found one Java-level deadlock: —— jstack 会自动检测并汇总死锁线程对及其锁等待关系
- 查看每个相关线程的完整堆栈,特别注意带 locked 和 waiting to lock 的行,它们指向同一个对象地址,说明存在变量级的锁传递依赖
- 结合线程栈中的源码行号(如有 debug 符号)、变量名(如 synchronized (lockA) 中的 lockA)反推是哪个业务变量在充当锁
典型死锁场景示例(变量链清晰可见)
假设有两个线程 T1 和 T2,分别执行:
T1: synchronized(lockA) { ... synchronized(lockB) { ... } }
T2: synchronized(lockB) { ... synchronized(lockA) { ... } }
jstack -l 输出中会出现:
"T1" ... locked (a java.lang.Object)
waiting to lock (a java.lang.Object)
"T2" ... locked (a java.lang.Object)
waiting to lock (a java.lang.Object)
这两个地址就是 lockA 和 lockB 在堆中的实例标识——它们构成了可追溯的变量链。只要在代码里找到持有这两个地址的对象(比如通过 MAT 分析 heap dump),就能定位到具体是哪两个成员变量或局部变量。
配合其他工具提升分析效率
- 用 jmap -histo
快速确认锁对象类型是否异常高频(如大量 java.lang.Object 实例) - 用 jmap -dump:format=b,file=heap.hprof
生成堆快照,再用 Eclipse MAT 搜索对应对象地址,查看其引用链和所属类字段名 - 若使用 ReentrantLock,jstack -l 会显示 - parking to wait for (a java.util.concurrent.locks.ReentrantLock$NonfairSync),此时锁变量通常是某个 ReentrantLock 字段,而非普通 Object











