隨多核 CPU 普及,Java 多線程並行計算普遍化,但不正確使用易導致死鎖、資源競爭癱瘓系統。JDK 1.5+ 引入 JVMTI (Java Virtual Machine Tool Interface) 取代 JVMPI/JVMDI,提供運行時監控、剖析、除錯能力。本文詳述如何基於 JVMTI 建構線程狀態切換監控代理。


new Thread(),空物件,未分配資源start() 分配資源 (除 CPU);yield() 釋放 CPU 重入就緒run()suspend()/sleep() 進阻塞集;同步區鎖競爭進鎖定集;wait() 進等待集run() 結束、interrupt()/stop() 異常退出線程物件顯式操作另一線程 (呼叫線程方法),三元組 <操作線程, 動作, 被操作線程>。涉及方法:start()、interrupt()、join()、resume()、suspend()、sleep()、yield()、stop()。
監控 start() 並分析 Java 呼叫堆疊可獲啟動上下文。
透過線程通訊原語 (wait()/notify()/notifyAll()) 產生,同樣用三元組描述。典型例:線程池 — 調度線程喚醒池中阻塞線程處理任務,完成後重入池待命。
確立三元組需分析通訊原語執行上下文,較顯式複雜。

Agent 透過 JVMTI 在方法呼叫者/被呼叫者間註冊事件監聽器:
Before Advice 回調 (處理呼叫前線程狀態)After Advice 回調 (處理呼叫後線程狀態)由此捕獲線程狀態切換全貌。
JVM 為每線程分配 Java 堆疊 (以幀為單位)。呼叫 Java 方法時壓入新堆疊幀 (含局部變量區、運算元堆疊、幀資料區)。
關鍵發現:實例方法執行時,JVM 隱式在當前幀局部變量區 索引 0 放入 this 參考 (表 1)。
| 索引 | 參數 |
|---|---|
| 0 | 對象引用 (this) |
| 1 | 區域變數 1 |
| 2 | 區域變數 2 |
因此,操作線程呼叫線程方法時,透過 GetLocalObject(thr, 0, 0, &thd_ptr) 即可從當前幀取得被操作線程物件參考,完成三元組構建。
例外:sleep()/yield() 為本地方法,不產生 Java 幀,資訊在本地堆疊,需另行分析。

Java 以監視器 同步多線程。每物件對應一監視器,同一時間僅一線程執行監視區域。線程進入監視區域前置入請求集,無競爭即獲監視器。執行 wait() 釋放監視器進等待集;notify() 標記等待集中某線程即將喚醒 (具體哪個由 VM 決定);notifyAll() 喚醒全部。線程必須持有監視器才能 wait(),且只能再次成為持有者離開等待集。
策略:在 wait()/notify()/notifyAll() 呼叫前後分別獲取等待集快照比對,差異即為被喚醒/阻塞線程。因線程可同時持有多監視器,需對每監視器等待集分析以確定真正作用的監視器。
#include "jvmti.h"
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
jvm->GetEnv((void **)&gb_jvmti, JVMTI_VERSION_1_0);
gb_capa.can_generate_method_exit_events = 1;
gb_capa.can_access_local_variables = 1;
gb_capa.can_get_monitor_info = 1;
gb_jvmti->AddCapabilities(&gb_capa);
callbacks.MethodEntry = &callbackMethodEntry;
callbacks.MethodExit = &callbackMethodExit;
gb_jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, NULL);
gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, NULL);
return JNI_OK;
}
GetEnv 取得 JVM 運行環境AddCapabilities 啟用:方法進出事件、存取區域變數、獲取監視器資訊SetEventCallbacks 註冊進入/退出回調SetEventNotificationMode 啟用事件通知callbackMethodEntry)
static void JNICALL callbackMethodEntry(jvmtiEnv *jvmti_env, JNIEnv* env, jthread thr, jmethodID method) {
gb_jvmti->RawMonitorEnter(gb_lock);
if (strcmp(name,"start")==0 || strcmp(name,"interrupt")==0 ||
strcmp(name,"join")==0 || strcmp(name,"stop")==0 ||
strcmp(name,"suspend")==0 || strcmp(name,"resume")==0) {
gb_jvmti->GetLocalObject(thr, 0, 0, &thd_ptr); // 索引 0 取 this
gb_jvmti->GetObjectHashCode(thd_ptr, &hash_code); // 雜湊值唯一標識線程
}
gb_jvmti->RawMonitorExit(gb_lock);
}
輸出格式:Thread-A, start, Thread-B、Thread-B, resume, Thread-C...
notify 為例)Method Entry 階段:獲取當前線程擁有的監視器 (GetOwnedMonitorInfo),再獲取各監視器等待線程 (GetObjectMonitorUsage),保存至鏈結串列待分析。
if (strcmp(name,"notify")==0 || strcmp(name,"notifyAll")==0) {
gb_jvmti->GetOwnedMonitorInfo(thr, &owned_monitor_count, &owned_monitors_ptr);
for (int i=0; iGetObjectMonitorUsage(*(owned_monitors_ptr+i), info_ptr);
insertElem(&inf_head, &inf_tail, info_ptr);
}
}
Method Exit 階段:再次獲取等待集 (notify_waiters),與先前快照比對,差異物件即為被操作線程。
if (info_ptr->notify_waiter_count != inf_head->info_ptr->notify_waiter_count) {
for (int i=0; iinfo_ptr->notify_waiter_count; i++) {
for (int j=0; jnotify_waiter_count; j++) {
if (inf_head->info_ptr->notify_waiters+i != info_ptr->notify_waiters+j) {
isObj = false; break;
}
}
if (isObj) {
GetObjectHashCode(*(inf_head->info_ptr->notify_waiters+i), &hash_code);
insertElem(&ctx_head, &ctx_tail, thr_hash_code, hash_code, name);
}
}
}
輸出:Thread-A, notify, Thread-B
編譯為動態鏈結庫,載入運行:
java -agentlib:ThreadMonitor JavaThreadPoolApp
# 或
java -agentpath:/<Path>/ThreadMonitor JavaThreadPoolApp
範例程式 JavaThreadPoolApp 實現線程池:啟動初始化兩子線程進 wait;客戶端需用時 notify 喚醒;用畢重入 wait。監控輸出範例:
Thread-32452561, start, Thread-60934352
Thread-32452561, start, Thread-89877242
Thread-60934352, wait, Thread-60934352, active 8 ms
Thread-89877242, wait, Thread-89877242, active 9 ms
Thread-32452561, notify, Thread-60934352
Thread-60934352, wait, Thread-60934352, active 3068 ms
結合應用資訊可知:客戶端使用線程池中 Thread-60934352 執行 3068 ms。
多線程開發挑戰多 (死鎖、資源競爭),JVMTI 提供即時監控分析 JVM 運行狀態的標準介面。本文:
此工具助開發人員診斷多線程應用中的線程調度問題,為生產環境疑難雜症排查提供有力支援。