Java线程死锁分析:如何定位死锁涉及的具体对象实例
嗨,我来帮你一步步理清这个死锁定位的问题~
问题1:0x00007f17b803cfc0 是什么?
这个十六进制值是JVM内部对象监视器(Monitor)的内存地址。在Java中,每个对象都会关联一个Monitor结构,它是实现synchronized内置锁的核心机制——当线程尝试获取某个对象的synchronized锁时,本质上就是在争夺这个Monitor的所有权。线程dump里的这个地址,就是当前线程正在等待的那个Monitor的内存标识。
不过要注意,线程dump里已经把Monitor和它对应的实际对象绑定了:比如你看到的waiting to lock monitor 0x00007f17b803cfc0 (object 0x00000000f72e7e90, a com.MyOtherClass),括号里的0x00000000f72e7e90才是你真正需要关注的目标对象实例的内存地址,Monitor只是和这个对象绑定的锁管理结构。
问题2:如何把这个地址和JVM里的具体对象关联起来,甚至拿到它的toString()输出?
这里核心是利用对象实例的内存地址(也就是上面提到的object后面的十六进制值),结合JVM工具来定位,具体方法如下:
生成并分析堆转储(Heap Dump)
这是最常用的方式:- 先用
jmap命令生成当前JVM的堆转储文件,命令格式是:jmap -dump:format=b,file=heapdump.hprof <你的Java进程ID> - 用内存分析工具打开这个
heapdump.hprof文件,比如VisualVM、Eclipse MAT或者IntelliJ IDEA自带的内存分析器。 - 在工具里搜索对象的内存地址(比如
0x00000000f72e7e90),就能找到对应的com.MyOtherClass实例。大部分工具会直接显示对象的toString()结果,或者你可以查看实例的字段值,手动拼接出业务标识。
- 先用
用JCMD命令直接查询对象信息
如果你使用的是较新的JDK版本(比如JDK8及以上),可以用jcmd工具直接通过对象地址查询信息:jcmd <你的Java进程ID> VM.objectdump <对象实例地址>这个命令会输出目标对象的详细信息,包括类名、字段值,部分JVM版本还会包含
toString()的结果。提前在代码里埋点优化(预防式方案)
如果可以提前准备,建议在com.MyClass和com.MyOtherClass这类容易引发锁竞争的类里重写toString()方法,把实例的关键业务标识(比如ID、唯一编号、业务属性)包含进去。这样后续一旦发生死锁,只要拿到堆转储,就能直接通过toString()快速定位是哪个业务实例出了问题,不用再去逐个查看字段。
备注:内容来源于stack exchange,提问作者MiamiBeach

