對比:Profiling 工具侵入性大、改變時序、不可用於生產;OS 工具(top/prstat)只見 CPU 佔用,不見 Java 鎖邏輯。
| 平台 | 命令 | 輸出位置 |
|---|---|---|
| Linux/Unix | kill -3 <PID> | 標準輸出/應用日志 |
| Windows 控制台 | Ctrl + Break | 控制台 |
| Java 6+ | jstack <PID> | 標準輸出 |
| Java 6+ GUI | jconsole → 線程頁籤 → 檢測死鎖 | 圖形化界面 |
大量線程等待同一把鎖(同一對象地址)是死鎖強信號:
"service-j2ee" prio=5 tid=0x024f1c28 nid=0x125 waiting for monitor entry
...
- waiting to lock <0x965d8110> (a com.sun.enterprise.resource.IASNonSharedResourcePool)
隔 1-2 分鐘再取一次 Dump,若等待線程集合不變,確認死鎖。搜索 locked <0x965d8110> 找到持鎖線程:
"Thread-20" daemon prio=5 tid=0x01394f18 nid=0x109 runnable
...
- locked <0x965d8110> (a com.sun.enterprise.resource.IASNonSharedResourcePool)
at com.sun.enterprise.resource.IASNonSharedResourcePool.resizePool(...)
...
at java.net.SocketInputStream.socketRead0(Native Method)
...
at oracle.net.ns.NetInputStream.read(...)
結論:持鎖線程在等待 Oracle 數據庫響應(網絡讀阻塞),導致連接池縮容鎖長期不釋放,引發級聯死鎖。
若持鎖線程又在等待另一把鎖(waiting to lock <0x...>),繼續搜索該地址的 locked,直到找到根因(通常是 I/O 阻塞、無限循環、外部系統無響應)。
"Thread-1" daemon ... in Object.wait()
- waiting on <0x95b07178> (a java.util.LinkedList)
- locked <0x95b07178> (a java.util.LinkedList)
at com.iplanet.ias.util.collection.BlockingQueue.remove(...)
此線程主動 wait() 釋放鎖,等待條件(隊列非空)。分析重點:等待條件是否會被滿足? 若生產者掛起,消費者永久等待。
jstack -l <PID>:打印鎖信息(java.util.concurrent.locks 也可見)jconsole:圖形化檢測 Object Monitor 和 JUC Locks 死鎖,支持導出jvisualvm + Threads 插件:時間軸視圖,更直觀BLOCKED/WAITING 線程數,識別熱點鎖Thread Dump 是 Java 死鎖排查的「核磁共振」。掌握 waiting for monitor entry ↔ locked 雙向追踪,配合多次快照對比,能精準定位 99% 的死鎖根因。建議將 Dump 分析納入事故應急預案,關鍵系統部署 jstack 定時采集腳本,做到有備無患。