首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JVM Bug 深度分析:線程 Dump 中多線程顯示持有同一把鎖的根因與修復

     編輯:Java 相關知識

現象:jstack 輸出中,BLOCKED 狀態線程的堆疊幀顯示 -locked <addr>,與持鎖線程完全一致,理論上只有持鎖線程才應顯示 -locked,等待線程應顯示 -waiting to lock。

根因追蹤

  1. jstack 發送 threaddump 命令 → Attach Listener 執行 VM_PrintThreads
  2. VMThread 在安全點執行:SafepointSynchronize::begin() 持有 Threads_lock
  3. 等待鎖的線程執行 ObjectMonitor::enter() → 設置 ThreadBlockInVM 狀態
  4. 關鍵競態:ThreadBlockInVM 檢測到安全點同步中(_synchronizing),調用 SafepointSynchronize::block() 卡在 Threads_lock
  5. 導致 Self->set_current_pending_monitor(this) 未執行
  6. Dump 時 javaVFrame::print_lock_info_on() 判斷 mark->monitor() != current_pending_monitor 為 false,誤判為持鎖

代碼證據

// ObjectMonitor::enter 關鍵片段
JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this); // 設置線程狀態 BLOCKED
// ... 省略 DTrace/JVMTI 埋點 ...
ThreadBlockInVM tbivm(jt); // 此處可能卡住
Self->set_current_pending_monitor(this); // 關鍵設置被跳過

// print_lock_info_on 判斷邏輯
if (mark->has_monitor() && mark->monitor() == thread->current_pending_monitor())
    lock_state = "waiting to lock";
else
    lock_state = "locked"; // 因 current_pending_monitor 為 NULL,誤入此分支

修復方案

將 Self->set_current_pending_monitor(this) 前移至 ThreadBlockInVM 之前:

Self->set_current_pending_monitor(this); // 先設置
ThreadBlockInVM tbivm(jt);              // 再進入安全點阻塞

影響評估

  • 純診斷顯示 Bug,不影響運行時正確性
  • 僅導致工具(jstack、VisualVM、Arthas)分析時誤導
  • 已在 OpenJDK 後續版本修復

排查方法論

現象 → 工具原理 → 源碼調用鏈 → 競態條件定位 → 最小化修復 → 迴歸驗證。養成「帶著問題查源碼」習慣,方能觸類旁通。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved