Java 虛擬機工具接口(Java Virtual Machine Tool Interface,JVMTI)提供了一種編程接口,允許軟件開發人員創建軟件代理以監視和控制 Java 編程語言應用程序。JVMTI 是 Java 2 Software Development Kit (SDK), Standard Edition, 版本 1.5.0 中的一種新增功能。它取代了 Java Virtual Machine Profiling Interface (JVMPI),從版本 1.1 起即作為 Java 2 SDK 的一種實驗功能包括在內。在 JSR-163 中對 JVMTI 進行了有關說明。
本文闡述如何使用 JVMTI 創建 Java 應用程序的調試和分析工具。這種工具(也稱作代理)在應用程序中發生事件時,能夠使用該接口提供的功能對事件通知進行注冊,并查詢和控制該應用程序。 這里 提供了 JVMTI 的文檔資料。JVMTI 代理對于調試和調優應用程序十分有用。它可以對應用程序的各個方面予以說明,如內存分配情況、CPU 利用情況及鎖爭奪情況。
?
盡管 JVMPI 現在仍處于實驗階段,很多 Java 技術開發人員已經在使用它了,而且已經把它應用到多種市場上提供的 Java 應用程序 Profiler。 請注意,極力鼓勵開發人員使用 JVMTI 而不使用 JVMPI。JVMPI 在不久的將來將被廢止。
?
JVMTI 在多個方面改進了 JVMPI 的功能和性能。例如:
- JVMTI 依賴于每個事件的回調。這比 JVMPI 設計使用需要編組和取消編組的事件結構更有效。
- JVMTI 包含四倍于 JVMPI 的函數(包括用于獲取關于變量、字段、方法和類的信息的更多函數)。有關 JVMTI 函數的完整索引,請參見 函數索引 頁。
- JVMTI 比 JVMPI 提供更多類型的事件通知,包括異常事件、字段訪問和修改事件、斷點和單步驟事件等。
- 有些從未被充分利用的 JVMPI 事件,如 Arena 的 new 和 delete,或者通過字節碼工具很容易就能獲得的內容,或者 JVMTI 函數本身(如 heap dump 和 object allocation)往往被 丟掉。 對這些事件的描述位于 事件索引 頁。
- JVMTI 是基于功能的,而 JVMPI 對于相應性能影響卻是“要么全有,要么全無”。
- JVMPI 堆功能不可伸縮。
- JVMPI 沒有錯誤返回信息。
- JVMPI 在 VM 實現方面具有很強的侵入性,容易導致維護問題和性能受損。
- JVMPI 是個實驗產品,不久將廢止。
?
在本文的以下部分,我們介紹一個簡單代理,它使用 JVMTI 函數從 Java 應用程序提取信息。 代理的編寫必須使用本地代碼。這里給出的示例代理是使用 C 語言編寫的。您可以 于此下載完整的示例代理代碼 。下面幾段介紹如何初始化一個代理,以及代理如何使用 JVMTI 函數提取關于 Java 應用程序的信息,以及如何編譯和運行代理。此示例代碼和編譯步驟特定于 UNIX 環境,但是經過修改后也可用于 Windows。這里介紹的代理可用于在任何 Java 應用程序中分析線程和確定 JVM 內存使用情況。
這里包含一個用 Java 語言編寫的簡單程序,稱作 SimpleThread.java ,并可 從這里下載 。我們使用 ThreadSample.java 演示此代理的預期輸出。
JVMTI 的功能很多,在此無法詳述;但本文中的代碼可以提供一個出發點,讓您去開發符合自己特定需求的分析工具。
代理初始化
本節介紹用于初始化代理的代碼。首先,代理必須包括
jvmti.h
文件,語句為
#include <jvmti.h>
。
另外,代理必須包含一個名為
Agent_OnLoad
的函數,加載庫時要調用這一函數。
Agent_OnLoad
函數用于在初始化 Java virtual machine (JVM) 之前設置所需的功能。
Agent_OnLoad
簽名如下所示:
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { ... /* We return JNI_OK to signify success */ return JNI_OK; } |
?
<!-- END VCD7 CODE SAMPLE COMPONENT -->
在我們的示例代碼中,我們必須為將要使用的 JVMTI 函數和事件啟用多種功能。一般情況下均需(在某些情況下必須)將這些功能添加到
Agent_OnLoad
函數中。有關每種函數或事件所需的功能的說明,參見
Java 虛擬機工具接口
頁。例如,要使用
InterruptThread
函數,
can_signal_thread
功能必須為 true。我們把示例所需的全部功能都設置為 true,然后使用
AddCapabilities
函數將它們添加到 JVMTI 環境中:
static jvmtiEnv *jvmti = NULL; static jvmtiCapabilities capa; jvmtiError error; ... (void)memset(&capa, 0, sizeof(jvmtiCapabilities)); capa.can_signal_thread = 1; capa.can_get_owned_monitor_info = 1; capa.can_generate_method_entry_events = 1; capa.can_generate_exception_events = 1; capa.can_generate_vm_object_alloc_events = 1; capa.can_tag_objects = 1; error = (*jvmti)->AddCapabilities(jvmti, &capa); check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities."); ... |
此外,
Agent_OnLoad
函數通常用于注冊事件通知。在此示例中,我們在使用
SetEventNotificationMode
函數的
Agent_OnLoad
中啟用了多個事件,如 VM Initialization Event、VM Death Event 和 VM Object Allocation, 如下所示
:
error = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, (jthread)NULL); error = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, (jthread)NULL); error = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, (jthread)NULL); check_jvmti_error(jvmti, error, "Cannot set event notification"); ... |
注意,在此示例中,NULL 是作為第三個參數傳遞的,它可以全局地啟用事件通知。如果需要,可以為某個特殊線程啟用或禁用某些事件。
我們為其注冊的每個事件還都必須具有一個指定的回調函數,當該事件發生時將調用它。例如,如果一個
Exception
類型的 JVMTI Event 發生,示例代理會將其發送到回調方法
callbackException()
中。
使用
jvmtiEventCallbacks
結構和
SetEventCallbacks
函數可以完成此任務:
jvmtiEventCallbacks callbacks; ... (void)memset(&callbacks, 0, sizeof(callbacks)); callbacks.VMInit = &callbackVMInit; /* JVMTI_EVENT_VM_INIT */ callbacks.VMDeath = &callbackVMDeath; /* JVMTI_EVENT_VM_DEATH */ callbacks.Exception = &callbackException;/* JVMTI_EVENT_EXCEPTION */ callbacks.VMObjectAlloc = &callbackVMObjectAlloc;/* JVMTI_EVENT_VM_OBJECT_ALLOC */ error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks,(jint)sizeof(callbacks)); check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks"); ... |
我們還將設置一個全局代理數據區域以在整個代碼中使用。
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
/* Global agent data structure */ typedef struct { /* JVMTI Environment */ jvmtiEnv *jvmti; jboolean vm_is_started; /* Data access Lock */ jrawMonitorID lock; } GlobalAgentData; static GlobalAgentData *gdata; |
在
Agent_OnLoad
函數中,我們執行以下設置:
/* Setup initial global agent data area * Use of static/extern data should be handled carefully here. * We need to make sure that we are able to cleanup after * ourselves so anything allocated in this library needs to be * freed in the Agent_OnUnload() function. */ static GlobalAgentData data; (void)memset((void*)&data, 0, sizeof(data)); gdata = &data; ... /* Here we save the jvmtiEnv* for Agent_OnUnload(). */ gdata->jvmti = jvmti; ... |
?
<!-- END VCD7 CODE SAMPLE COMPONENT -->
我們在
Agent_OnLoad()
中創建一個原始監視器,然后把代碼
VM_INIT、VM_DEATH
和 EXCEPTION
包裝于 JVMTI
RawMonitorEnter()
和
RawMonitorExit()
接口 。
/* Here we create a raw monitor for our use in this agent to * protect critical sections of code. */ error = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &(gdata->lock)); /* Enter a critical section by doing a JVMTI Raw Monitor Enter */ static void enter_critical_section(jvmtiEnv *jvmti) { jvmtiError error; error = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock); check_jvmti_error(jvmti, error, "Cannot enter with raw monitor"); } /* Exit a critical section by doing a JVMTI Raw Monitor Exit */ static void exit_critical_section(jvmtiEnv *jvmti) { jvmtiError error; error = (*jvmti)->RawMonitorExit(jvmti, gdata->lock); check_jvmti_error(jvmti, error, "Cannot exit with raw monitor"); } |
卸載代理時
,VM
將調用
Agent_OnUnload
。此函數用于清理在
Agent_OnLoad
期間分配的資源。
/* Agent_OnUnload: This is called immediately before the shared library * is unloaded. This is the last code executed. */ JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) { /* Make sure all malloc/calloc/strdup space is freed */ } |
使用 JVMTI 分析線程
本節介紹如何獲取關于在 JVM 中運行的用戶線程的信息。如前所述,啟動 JVM 時,JVMTI 代理庫中的啟動函數
Agent_OnLoad
將被調用。在 VM 初始化過程中,
JVMTI_EVENT_VM_INIT
類型的 JVMTI Event 將生成并被發送到代理代碼的
callbackVMInit
例程中。一旦 VM 初始化事件被接收(即
調用VMInit
回調),代理即可結束其初始化。現在,此代理可以自由調用任何 Java Native Interface (JNI) 或 JVMTI 函數。此時,我們已經處于活動階段,將啟用本 VMInit 回調例程中的
Exception
事件(
JVMTI_EVENT_EXCEPTION
)。
error = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, (jthread)NULL); |
無論何時,只要在
Java
編程語言方法中首次探測到異常
,
就會生成
Exception
事件。此異常可能由 Java 編程語言拋出,也可能由本地方法拋出;但是如果由本地方法拋出,直到 Java 編程語言方法首次發現此異常時該事件才會生成。如果異常已被處理并清除,則異常事件不會生成。
出于演示目的,下面給出了所用的示例 Java 應用程序。主線程創建了 5 個線程,這 5 個線程退出前各自拋出一個異常。一旦啟動 JVM
,JVMTI_EVENT_VM_INIT
將生成并被發送到代理代碼中進行處理,因為我們已經在代理代碼中啟用
了 VMInit
和 Exception
事件。隨后,當 Java 線程拋出一個異常時,
JVMTI_EVENT_EXCEPTION
將被發送到代理代碼中。然后,代理代碼 會分析此線程信息并顯示當前線程名、它所屬的線程組、此線程所擁有的監視器、線程狀態、線程堆棧跟蹤及 JVM 中的所有用戶線程。
public class SimpleThread { static MyThread t; public static void main(String args[]) throws Throwable{ t = new MyThread(); System.out.println("Creating and running 10 threads..."); for(int i = 0; i < 5; i++) { Thread thr = new Thread(t,"MyThread"+i); thr.start(); try { thr.join(); } catch (Throwable t) { } } } } class MyThread implements Runnable { Thread t; public MyThread() { } public void run() { /* NO-OP */ try { "a".getBytes("ASCII"); throwException(); Thread.sleep(1000); } catch (java.lang.InterruptedException e){ e.printStackTrace(); } catch (Throwable t) { } } public void throwException() throws Throwable{ throw new Exception("Thread Exception from MyThread"); } } |
我們來看一下 Java 應用程序內部拋出一個異常時 JVMTI 代理代碼的執行情況。
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
throw new Exception("Thread Exception from MyThread"); |
JVMTI 異常事件生成后將被發送到代理代碼的
Exception
回調例程中。代理必須添加
can_generate_exception_events
功能才能啟用異常事件。我們使用 JVMTI
GetMethodName
接口來顯示生成異常的方法名和例程簽名。
err3 = (*jvmti)->GetMethodName(jvmti, method, &name, &sig, &gsig); printf("Exception in Method:%s%s\n", name, sig); |
我們使用 JVMTI
GetThreadInfo
和 GetThreadGroupInfo
接口來顯示當前線程和組詳細信息。
err = (*jvmti)->GetThreadInfo(jvmti, thr, &info); if (err == JVMTI_ERROR_NONE) { err1 = (*jvmti)->GetThreadGroupInfo(jvmti,info.thread_group, &groupInfo); ... if ((err == JVMTI_ERROR_NONE) && (err1 == JVMTI_ERROR_NONE )) { printf("Got Exception event, Current Thread is : %s and Thread Group is: %s\n", ((info.name==NULL) ? "" : info.name), groupInfo.name); } } |
這將在您的終端上產生以下輸出:
Got Exception event, Current Thread is : MyThread0 and Thread Group is: main
使用 JVMTI
GetOwnedMonitorInfo
接口可以獲取關于指定線程所擁有的監視器的信息。此函數 不要求掛起線程。
err = (*jvmti)->GetOwnedMonitorInfo(jvmti, thr, νm_monitors, &arr_monitors); printf("Number of Monitors returned : %d\n", num_monitors); |
使用 JVMTI
GetThreadState
接口可以獲取線程的狀態信息。
線程狀態可以為以下值之一:
- 線程已終止
- 線程活動
- 線程可運行
- 線程休眠
- 線程在等待通知
- 線程處于對象等待狀態
- 線程為本地狀態
- 線程已掛起
- 線程已中斷
err = (*jvmti)->GetThreadState(jvmti, thr, &thr_st_ptr); if ( thr_st_ptr & JVMTI_THREAD_STATE_RUNNABLE ) { printf("Thread: %s is Runnable\n", ((info.name==NULL) ? "" : info.name)); flag = 1; } |
|
JVMTI 函數
GetAllThreads
用于顯示 JVM 已知的所有活動線程。這些線程是關聯到 VM 的 Java 編程語言線程。
以下代碼對此進行了說明:
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
/* Get All Threads */ err = (*jvmti)->GetAllThreads(jvmti, &thr_count, &thr_ptr); if (err != JVMTI_ERROR_NONE) { printf("(GetAllThreads) Error expected: %d, got: %d\n", JVMTI_ERROR_NONE, err); describe(err); printf("\n"); } if (err == JVMTI_ERROR_NONE && thr_count >= 1) { int i = 0; printf("Thread Count: %d\n", thr_count); for ( i=0; i < thr_count; i++) { /* Make sure the stack variables are garbage free */ (void)memset(&info1,0, sizeof(info1)); err1 = (*jvmti)->GetThreadInfo(jvmti, thr_ptr[i], &info1); if (err1 != JVMTI_ERROR_NONE) { printf("(GetThreadInfo) Error expected: %d, got: %d\n", JVMTI_ERROR_NONE, err1); describe(err1); printf("\n"); } printf("Running Thread#%d: %s, Priority: %d, context class loader:%s\n", i+1,info1.name, info1.priority,(info1.context_class_loader == NULL ? ": NULL" : "Not Null")); /* Every string allocated by JVMTI needs to be freed */ err2 = (*jvmti)->Deallocate(jvmti, (void*)info1.name); if (err2 != JVMTI_ERROR_NONE) { printf("(GetThreadInfo) Error expected: %d, got: %d\n", JVMTI_ERROR_NONE, err2); describe(err2); printf("\n"); } } } |
這將在您的終端上產生以下輸出:
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
Thread Count: 5 Running Thread#1: MyThread4, Priority: 5, context class loader:Not Null Running Thread#2: Signal Dispatcher, Priority: 10, context class loader:Not Null Running Thread#3: Finalizer, Priority: 8, context class loader:: NULL Running Thread#4: Reference Handler, Priority: 10, context class loader:: NULL Running Thread#5: main, Priority: 5, context class loader:Not Null |
|
JVMTI 接口
GetStackTrace
可用于獲取關于線程堆棧的信息。如果
max_count
小于堆棧的深度,最深框架的
max_count
數將返回,否則返回整個堆棧。調用此函數無需掛起線程。
下例產生至多 5 個最深框架。如果存在任何框架,則還將輸出當前執行的方法名。
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
/* Get Stack Trace */ err = (*jvmti)->GetStackTrace(jvmti, thr, 0, 5, &frames, &count); if (err != JVMTI_ERROR_NONE) { printf("(GetThreadInfo) Error expected: %d, got: %d\n", JVMTI_ERROR_NONE, err); describe(err); printf("\n"); } printf("Number of records filled: %d\n", count); if (err == JVMTI_ERROR_NONE && count >=1) { char *methodName; methodName = "yet_to_call()"; char *declaringClassName; jclass declaring_class; int i=0; printf("Exception Stack Trace\n"); printf("=====================\n"); printf("Stack Trace Depth: %d\n", count); for ( i=0; i < count; i++) { err = (*jvmti)->GetMethodName (jvmti, frames[i].method, &methodName, NULL, NULL); if (err == JVMTI_ERROR_NONE) { err = (*jvmti)->GetMethodDeclaringClass(jvmti, frames[i].method, &declaring_class); err = (*jvmti)->GetClassSignature(jvmti, declaring_class, &declaringClassName, NULL); if (err == JVMTI_ERROR_NONE) { printf("at method %s() in class %s\n", methodName, declaringClassName); } } } } |
這將使您的終端產生以下輸出:
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
Number of records filled: 3 Thread Stack Trace ===================== Stack Trace Depth: 3 at method throwException() in class LmyThread; at method run() in class LMyThread; at method run() in class Ljava/lang/Thread; |
使用 JVMTI 分析堆
本節介紹如何獲取關于使用堆的信息的示例代碼。例如,我們已經按“代理初始化”一節中所述為 VM Object Allocation 事件進行了注冊。當 JVM 分配了 Java 編程語言可見但其他工具機制不能探測到的對象時,我們將得到通知。這一點與 JVMPI 截然不同,JVMPI 在分配任何對象時都將發送事件。在 JVMTI 中,針對用戶分配的對象不會發送任何事件,因為它期望使用的是字節碼工具。例如,在
SimpleThread.java
程序中,分配
MyThread
或 Thread
對象時,我們是不會得到通知的。以后將單獨發表一篇文章,描寫如何使用字節碼工具獲取此信息。
VM Object Allocation 事件對于確定有關由 JVM 分配的對象的信息十分有用。在
Agent_OnLoad
方法中,我們將
callbackVMObjectAlloc
注冊為發送 VM Object Allocation 事件時調用的函數。回調函數參數包含關于已分配對象的信息,如對象類和對象大小的 JNI 本地參考。借助于
jclass
參數
object_klass
,我們可以使用
GetClassSignature
函數獲取關于類名的信息。我們可以把下面給出的對象類及其大小打印出來。注意避免過多的輸出,我們僅需輸出超過 50 個字節的對象信息就行了。
/* Callback function for VM Object Allocation events */ static void JNICALL callbackVMObjectAlloc (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jobject object, jclass object_klass, jlong size) { ... char *className; ... if (size > 50) { err = (*jvmti)->GetClassSignature(jvmti, object_klass, &className, NULL); if (className != NULL) { printf("\ntype %s object allocated with size %d\n", className, (jint)size); } ... |
我們使用
上面
所介紹的
GetStackTrace
方法來輸出正在分配該對象的線程的堆棧跟蹤。我們依照該節所述獲取指定深度的 框架。這些框架將作為
jvmtiFrameInfo
結構返回,這些結構包含每個框架的
jmethodID
(即
frames[x].method
)。
GetMethodName
函數可以將
jmethodID
映射到特殊的方法名中。在此示例的最后部分,我們還將使用
GetMethodDeclaringClass
和 GetClassSignature
函數獲取從其中調用過此方法的類的名稱。
char *methodName; char *declaringClassName; jclass declaring_class; jvmtiError err; //print stack trace jvmtiFrameInfo frames[5]; jint count; int i; err = (*jvmti)->GetStackTrace(jvmti, NULL, 0, 5, &frames, &count); if (err == JVMTI_ERROR_NONE && count >= 1) { for (i = 0; i < count; i++) { err = (*jvmti)->GetMethodName(jvmti, frames[i].method, &methodName, NULL, NULL); if (err == JVMTI_ERROR_NONE) { err = (*jvmti)->GetMethodDeclaringClass(jvmti, frames[i].method, &declaring_class); err = (*jvmti)->GetClassSignature(jvmti, declaring_class, &declaringClassName, NULL); if (err == JVMTI_ERROR_NONE) { printf("at method %s in class %s\n", methodName, declaringClassName); } } } } ... |
注意,完成任務時應釋放由這些函數分配給
char
數組的內存:
err = (*jvmti)->Deallocate(jvmti, (void*)className); err = (*jvmti)->Deallocate(jvmti, (void*)methodName); err = (*jvmti)->Deallocate(jvmti, (void*)declaringClassName); ... |
此代碼的輸出如下所示:
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
type Ljava/lang/reflect/Constructor; object allocated with size 64 at method getDeclaredConstructors0 in class Ljava/lang/Class; at method privateGetDeclaredConstructors in class Ljava/lang/Class; at method getConstructor0 in class Ljava/lang/Class; at method getDeclaredConstructor in class Ljava/lang/Class; at method run in class Ljava/util/zip/ZipFile$1; |
原始類的返回名稱是相應原始類型的簽名字符類型。例如,
java.lang.Integer.TYPE
為“I”。
在 VM Object Allocation 的回調方法中,我們仍將使用
IterateOverObjectsReachableFromObject
函數演示如何獲取關于堆的附加信息。在此示例中,我們將 JNI 參考作為一個參數傳遞給剛剛分配的對象,該函數將在此新分配對象所能直接或間接到達的所有對象中迭代。對于每個可到達的對象,另外還有一個定義的回調函數可對其進行描述。在此示例中,傳遞到
IterateOverObjectsReachableFromObject
的回調函數名為
reference_object
:
err = (*jvmti)->IterateOverObjectsReachableFromObject (jvmti, object, &reference_object, NULL); if ( err != JVMTI_ERROR_NONE ) { printf("Cannot iterate over reachable objects\n"); } ... |
reference_object
函數定義如下:
/* JVMTI callback function. */ static jvmtiIterationControl JNICALL reference_object(jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong referrer_tag, jint referrer_index, void *user_data) { ... return JVMTI_ITERATION_CONTINUE; } ... |
在此示例中,我們使用
IterateOverObjectsReachableFromObject
函數計算新分配對象所能到達的所有對象的 總的大小,以及它們的對象類型。對象類型可以從
reference_kind
參數中確定。然后打印此信息以接收如下輸出:
This object has references to objects of combined size 21232 This includes 45 classes, 9 fields, 1 arrays, 0 classloaders, 0 signers arrays, 0 protection domains, 19 interfaces, 13 static fields, and 2 constant pools. |
注意,位于 JVMTI 中的類似迭代函數允許迭代的對象有:整個堆(可到達的和不可到達的);根目錄對象和根目錄對象所能直接或間接到達的所有對象;堆中是指定類的實例的所有對象。使用這些函數的技巧和前面所介紹的類似。在執行這些函數期間,堆的狀態沒有任何變化:沒有分配任何對象,沒有對任何對象進行垃圾收集,并且對象的狀態(包括堆值)也沒有任何變化。結果,執行 Java 編程語言代碼的線程、嘗試恢復執行 Java 編程語言代碼的線程和嘗試執行 JNI 函數的線程都完全停了下來。所以,在對象參考回調函數中,不能使用任何 JNI 函數;在沒有特別允許的情況下,也不允許使用任何 JVMTI 函數。
編譯和執行示例代碼
要編譯并運行這里描述的示例應用程序的代碼,請按以下步驟操作:
-
設置 JDK_PATH 為指向 J2SE 1.5 發行版
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->JDK_PATH="/home/xyz/j2sdk1.5.0/bin"
<!-- END VCD7 CODE SAMPLE COMPONENT --> -
使用 C 語言編譯器構建共享庫。我們使用的是 Sun Studio 8 C 編譯器。
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->CC="/net/compilers/S1Studio_8.0/SUNWspro/bin/cc" echo "...creating liba.so" ${CC} -G -KPIC -o liba.so -I${JDK_PATH}/include -I${JDK_PATH}/include/solaris a.c
<!-- END VCD7 CODE SAMPLE COMPONENT --> -
要加載并運行代理庫,請在 VM 啟動過程中使用下面的命令行參數之一。
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->-agentlib:<jvmti-agent-library-name> -agentpath:/home/foo/jvmti/<jvmti-agent-library-name>
<!-- END VCD7 CODE SAMPLE COMPONENT -->然后如下運行示例 Java 應用程序:
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->echo "...creating SimpleThread.class" ${JDK_PATH}/bin/javac -g -d . SimpleThread.java echo "...running SimpleThread.class" LD_LIBRARY_PATH=. CLASSPATH=. ${JDK_PATH}/bin/java -showversion -agentlib:a SimpleThread
<!-- END VCD7 CODE SAMPLE COMPONENT -->
注意:此示例代理代碼是在 Solaris 9 Operating System 上構建和測試的。
結束語
在本文中,我們演示了 JVMTI 提供用于監控和管理 JVM 的一些接口。JVMTI 規范 (JSR-163) 旨在為需要訪問 VM 狀態的廣泛的工具提供一個 VM 接口,這些工具包括但不限于:分析、調試、監控、線程分析和覆蓋率分析工具。
建議開發人員不要使用 JVMPI 接口開發工具或調試實用工具,因為 JVMPI 是一種不受支持的實驗技術。應考慮使用 JVMTI 編寫 Java 虛擬機的所有分析和管理工具。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
