Java Platform Debugger Architecture (JPDA) 由三層組成:
本文聚焦 JVMTI:它是 JVMPI(Profiling)與 JVMDI(Debug)的統一繼任者,自 Java 5 起成為標準,支援調試、性能分析、線程分析、覆蓋率統計等場景。
Agent 是 JVMTI 的客戶端,以動態庫形式運行在目標 JVM 進程內,受外部進程控制,充當中介角色。

| 模式 | 啟動參數 | 觸發時機 | 入口函數 |
|---|---|---|---|
| 啟動加載 | -agentlib:名稱=選項 或 -agentpath:路徑=選項 | JVM 初始化早期(類未載入、對象未創建、代碼未執行) | Agent_OnLoad(JavaVM*, char*, void*) |
| 運行時加載 | 通過 Attach API 動態附著 | JVM 運行中 | Agent_OnAttach(JavaVM*, char*, void*) |
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
jvmtiEnv *jvmti;
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0); // 獲取 JVMTI 環境指針
// 1. 解析命令行選項 options
// 2. 查詢/添加 Capabilities(如 can_redefine_any_class、can_generate_method_entry_events)
// 3. 設置事件回調
// 4. 啟用事件通知
return JNI_OK;
}
關鍵點:
GetEnv 版本號必須與 JVM 支持的 JVMTI 版本匹配。options 字串僅在入口函數有效,需自行複製保存。SuspendThread 需 JVM 處於 live phase)。JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
// 釋放資源、移除回調、清理標記內存
}
jvmtiCapabilities capa;
jvmtiError err = (*jvmti)->GetCapabilities(jvmti, &capa); // 查詢當前能力
if (err == JVMTI_ERROR_NONE && capa.can_redefine_any_class) { ... }
jvmtiCapabilities potential;
err = (*jvmti)->GetPotentialCapabilities(jvmti, &potential); // 查詢可選能力
if (err == JNI_OK) err = (*jvmti)->AddCapabilities(jvmti, &potential); // 一次性啟用所有可用能力
不同 JVM 實現對動態添加 Capabilities 的限制不同,建議在 Agent_OnLoad 階段完成配置。
jvmtiError err = (*jvmti)->SomeJVMTIFunction(...);
if (err != JVMTI_ERROR_NONE) {
// 根據錯誤碼處理:JVMTI_ERROR_INVALID_THREAD、JVMTI_ERROR_NOT_AVAILABLE 等
}
註冊回調函數至 jvmtiEventCallbacks 結構體,再調用 SetEventCallbacks 與 SetEventNotificationMode 啟用。常見事件:
VM_INIT、VM_START、VM_DEATHCLASS_LOAD、CLASS_PREPAREMETHOD_ENTRY、METHOD_EXITTHREAD_START、THREAD_ENDBREAKPOINT、SINGLE_STEP併發注意:回調可能並發執行(多線程同時觸發同一事件),需自行處理同步;回調中拋出異常會導致原有異常丟失。
JVMTI 不提供直接對象查找,採用迭代器模式:
// 遍歷整個堆或從指定根對象追蹤引用
jvmtiError IterateThroughHeap(jvmtiEnv* env, jint heap_filter, jclass klass,
const jvmtiHeapCallbacks* callbacks, const void* user_data);
jvmtiError FollowReferences(jvmtiEnv* env, jint heap_filter, jclass klass,
jobject initial_object, const jvmtiHeapCallbacks* callbacks,
const void* user_data);
回調中對滿足條件的對象打 tag,再用 GetObjectsWithTags 批量獲取。避免註冊不必要的原始類型回調,否則效率數量級下降。
GetOwnedMonitorInfo:獲取線程持有的鎖。GetCurrentContendedMonitor:獲取線程正在等待的鎖,輔助死鎖檢測。CreateRawMonitor、RawMonitorEnter/Exit、RawMonitorWait/Notify):Native 層同步原語。jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
// location 為方法字節碼索引,觸發 BREAKPOINT 事件
class MethodTraceAgent {
static jvmtiEnv *m_jvmti;
static char* m_filter; // 方法名過濾參數
void Init(JavaVM*) const; // 獲取 jvmtiEnv、設置版本
void ParseOptions(const char*) const; // 解析 "first" 等參數
void AddCapability() const; // 啟用 can_generate_method_entry_events
void RegisterEvent() const; // 註冊 METHOD_ENTRY 回調
static void JNICALL HandleMethodEntry(jvmtiEnv*, JNIEnv*, jthread, jmethodID);
};

# Windows (MSVC)
cl /EHsc -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD MethodTraceAgent.cpp Main.cpp -FeAgent.dll
# Linux (g++)
g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared MethodTraceAgent.cpp Main.cpp -o libagent.so
測試類 MethodTraceTest 包含 first()、second() 方法。運行對比:
java -agentlib:Agent=first MethodTraceTest:Agent 過濾參數 first,僅輸出 first 方法進入事件。java -agentlib:Agent MethodTraceTest:無過濾,輸出所有方法進入(含類庫初始化方法)。

JVMTI 通過 Agent 機制賦予開發者對 JVM 運行時的完全可觀測性與可控性。掌握 Capabilities 管理、事件回調、堆遍歷、線程鎖操作等核心 API,即可構建定製化的 Profiling、覆蓋率分析、熱代碼替換等高級工具。下一篇將深入 JDWP 協議,揭示調試器前端如何通過標準通訊層與後端 JVMTI 交互。