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

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

















