欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

使用 Java 虛擬機(jī)工具接口(JVMTI)創(chuàng)建調(diào)試和

系統(tǒng) 2042 0

Java 虛擬機(jī)工具接口(Java Virtual Machine Tool Interface,JVMTI)提供了一種編程接口,允許軟件開發(fā)人員創(chuàng)建軟件代理以監(jiān)視和控制 Java 編程語言應(yīng)用程序。JVMTI 是 Java 2 Software Development Kit (SDK), Standard Edition, 版本 1.5.0 中的一種新增功能。它取代了 Java Virtual Machine Profiling Interface (JVMPI),從版本 1.1 起即作為 Java 2 SDK 的一種實(shí)驗(yàn)功能包括在內(nèi)。在 JSR-163 中對(duì) JVMTI 進(jìn)行了有關(guān)說明。

本文闡述如何使用 JVMTI 創(chuàng)建 Java 應(yīng)用程序的調(diào)試和分析工具。這種工具(也稱作代理)在應(yīng)用程序中發(fā)生事件時(shí),能夠使用該接口提供的功能對(duì)事件通知進(jìn)行注冊(cè),并查詢和控制該應(yīng)用程序。 這里 提供了 JVMTI 的文檔資料。JVMTI 代理對(duì)于調(diào)試和調(diào)優(yōu)應(yīng)用程序十分有用。它可以對(duì)應(yīng)用程序的各個(gè)方面予以說明,如內(nèi)存分配情況、CPU 利用情況及鎖爭(zhēng)奪情況。

?

盡管 JVMPI 現(xiàn)在仍處于實(shí)驗(yàn)階段,很多 Java 技術(shù)開發(fā)人員已經(jīng)在使用它了,而且已經(jīng)把它應(yīng)用到多種市場(chǎng)上提供的 Java 應(yīng)用程序 Profiler。 請(qǐng)注意,極力鼓勵(lì)開發(fā)人員使用 JVMTI 而不使用 JVMPI。JVMPI 在不久的將來將被廢止。

?

JVMTI 在多個(gè)方面改進(jìn)了 JVMPI 的功能和性能。例如:

  • JVMTI 依賴于每個(gè)事件的回調(diào)。這比 JVMPI 設(shè)計(jì)使用需要編組和取消編組的事件結(jié)構(gòu)更有效。
  • JVMTI 包含四倍于 JVMPI 的函數(shù)(包括用于獲取關(guān)于變量、字段、方法和類的信息的更多函數(shù))。有關(guān) JVMTI 函數(shù)的完整索引,請(qǐng)參見 函數(shù)索引 頁。
  • JVMTI 比 JVMPI 提供更多類型的事件通知,包括異常事件、字段訪問和修改事件、斷點(diǎn)和單步驟事件等。
  • 有些從未被充分利用的 JVMPI 事件,如 Arena 的 new 和 delete,或者通過字節(jié)碼工具很容易就能獲得的內(nèi)容,或者 JVMTI 函數(shù)本身(如 heap dump 和 object allocation)往往被 丟掉。 對(duì)這些事件的描述位于 事件索引 頁。
  • JVMTI 是基于功能的,而 JVMPI 對(duì)于相應(yīng)性能影響卻是“要么全有,要么全無”。
  • JVMPI 堆功能不可伸縮。
  • JVMPI 沒有錯(cuò)誤返回信息。
  • JVMPI 在 VM 實(shí)現(xiàn)方面具有很強(qiáng)的侵入性,容易導(dǎo)致維護(hù)問題和性能受損。
  • JVMPI 是個(gè)實(shí)驗(yàn)產(chǎn)品,不久將廢止。

?

在本文的以下部分,我們介紹一個(gè)簡(jiǎn)單代理,它使用 JVMTI 函數(shù)從 Java 應(yīng)用程序提取信息。 代理的編寫必須使用本地代碼。這里給出的示例代理是使用 C 語言編寫的。您可以 于此下載完整的示例代理代碼 。下面幾段介紹如何初始化一個(gè)代理,以及代理如何使用 JVMTI 函數(shù)提取關(guān)于 Java 應(yīng)用程序的信息,以及如何編譯和運(yùn)行代理。此示例代碼和編譯步驟特定于 UNIX 環(huán)境,但是經(jīng)過修改后也可用于 Windows。這里介紹的代理可用于在任何 Java 應(yīng)用程序中分析線程和確定 JVM 內(nèi)存使用情況。

這里包含一個(gè)用 Java 語言編寫的簡(jiǎn)單程序,稱作 SimpleThread.java ,并可 從這里下載 。我們使用 ThreadSample.java 演示此代理的預(yù)期輸出。

JVMTI 的功能很多,在此無法詳述;但本文中的代碼可以提供一個(gè)出發(fā)點(diǎn),讓您去開發(fā)符合自己特定需求的分析工具。

代理初始化

本節(jié)介紹用于初始化代理的代碼。首先,代理必須包括 jvmti.h 文件,語句為 #include <jvmti.h>

另外,代理必須包含一個(gè)名為 Agent_OnLoad 的函數(shù),加載庫時(shí)要調(diào)用這一函數(shù)。 Agent_OnLoad 函數(shù)用于在初始化 Java virtual machine (JVM) 之前設(shè)置所需的功能。 Agent_OnLoad 簽名如下所示:

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
             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 函數(shù)和事件啟用多種功能。一般情況下均需(在某些情況下必須)將這些功能添加到 Agent_OnLoad 函數(shù)中。有關(guān)每種函數(shù)或事件所需的功能的說明,參見 Java 虛擬機(jī)工具接口 頁。例如,要使用 InterruptThread 函數(shù), can_signal_thread 功能必須為 true。我們把示例所需的全部功能都設(shè)置為 true,然后使用 AddCapabilities 函數(shù)將它們添加到 JVMTI 環(huán)境中:

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
             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.");

            ...

            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

此外, Agent_OnLoad 函數(shù)通常用于注冊(cè)事件通知。在此示例中,我們?cè)谑褂? SetEventNotificationMode 函數(shù)的 Agent_OnLoad 中啟用了多個(gè)事件,如 VM Initialization Event、VM Death Event 和 VM Object Allocation, 如下所示

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
             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");

            ...
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

注意,在此示例中,NULL 是作為第三個(gè)參數(shù)傳遞的,它可以全局地啟用事件通知。如果需要,可以為某個(gè)特殊線程啟用或禁用某些事件。

我們?yōu)槠渥?cè)的每個(gè)事件還都必須具有一個(gè)指定的回調(diào)函數(shù),當(dāng)該事件發(fā)生時(shí)將調(diào)用它。例如,如果一個(gè) Exception 類型的 JVMTI Event 發(fā)生,示例代理會(huì)將其發(fā)送到回調(diào)方法 callbackException() 中。

使用 jvmtiEventCallbacks 結(jié)構(gòu)和 SetEventCallbacks 函數(shù)可以完成此任務(wù):

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            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");

            ...

            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

我們還將設(shè)置一個(gè)全局代理數(shù)據(jù)區(qū)域以在整個(gè)代碼中使用。

<!-- 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;
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

Agent_OnLoad 函數(shù)中,我們執(zhí)行以下設(shè)置:

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            /* 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 -->

我們?cè)? Agent_OnLoad() 中創(chuàng)建一個(gè)原始監(jiān)視器,然后把代碼 VM_INIT、VM_DEATH 和 EXCEPTION 包裝于 JVMTI RawMonitorEnter() RawMonitorExit() 接口 。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            /* 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");

            }
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

卸載代理時(shí) ,VM 將調(diào)用 Agent_OnUnload 。此函數(shù)用于清理在 Agent_OnLoad 期間分配的資源。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            /* 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 */

            }
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

使用 JVMTI 分析線程

本節(jié)介紹如何獲取關(guān)于在 JVM 中運(yùn)行的用戶線程的信息。如前所述,啟動(dòng) JVM 時(shí),JVMTI 代理庫中的啟動(dòng)函數(shù) Agent_OnLoad 將被調(diào)用。在 VM 初始化過程中, JVMTI_EVENT_VM_INIT 類型的 JVMTI Event 將生成并被發(fā)送到代理代碼的 callbackVMInit 例程中。一旦 VM 初始化事件被接收(即 調(diào)用VMInit 回調(diào)),代理即可結(jié)束其初始化?,F(xiàn)在,此代理可以自由調(diào)用任何 Java Native Interface (JNI) 或 JVMTI 函數(shù)。此時(shí),我們已經(jīng)處于活動(dòng)階段,將啟用本 VMInit 回調(diào)例程中的 Exception 事件( JVMTI_EVENT_EXCEPTION )。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            error = (*jvmti)->SetEventNotificationMode

            (jvmti, JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, (jthread)NULL);
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

無論何時(shí),只要在 Java 編程語言方法中首次探測(cè)到異常 , 就會(huì)生成 Exception 事件。此異??赡苡?Java 編程語言拋出,也可能由本地方法拋出;但是如果由本地方法拋出,直到 Java 編程語言方法首次發(fā)現(xiàn)此異常時(shí)該事件才會(huì)生成。如果異常已被處理并清除,則異常事件不會(huì)生成。

出于演示目的,下面給出了所用的示例 Java 應(yīng)用程序。主線程創(chuàng)建了 5 個(gè)線程,這 5 個(gè)線程退出前各自拋出一個(gè)異常。一旦啟動(dòng) JVM ,JVMTI_EVENT_VM_INIT 將生成并被發(fā)送到代理代碼中進(jìn)行處理,因?yàn)槲覀円呀?jīng)在代理代碼中啟用 了 VMInit 和 Exception 事件。隨后,當(dāng) Java 線程拋出一個(gè)異常時(shí), JVMTI_EVENT_EXCEPTION 將被發(fā)送到代理代碼中。然后,代理代碼 會(huì)分析此線程信息并顯示當(dāng)前線程名、它所屬的線程組、此線程所擁有的監(jiān)視器、線程狀態(tài)、線程堆棧跟蹤及 JVM 中的所有用戶線程。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
             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");

            }

            } 
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

我們來看一下 Java 應(yīng)用程序內(nèi)部拋出一個(gè)異常時(shí) JVMTI 代理代碼的執(zhí)行情況。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
             throw new Exception("Thread Exception from MyThread");
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

JVMTI 異常事件生成后將被發(fā)送到代理代碼的 Exception 回調(diào)例程中。代理必須添加 can_generate_exception_events 功能才能啟用異常事件。我們使用 JVMTI GetMethodName 接口來顯示生成異常的方法名和例程簽名。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            err3 = (*jvmti)->GetMethodName(jvmti, method, &name, &sig, &gsig);

            printf("Exception in Method:%s%s\n", name, sig);
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

我們使用 JVMTI GetThreadInfo 和 GetThreadGroupInfo 接口來顯示當(dāng)前線程和組詳細(xì)信息。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            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);

            }

            }
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

這將在您的終端上產(chǎn)生以下輸出:

Got Exception event, Current Thread is : MyThread0 and Thread Group is: main

使用 JVMTI GetOwnedMonitorInfo 接口可以獲取關(guān)于指定線程所擁有的監(jiān)視器的信息。此函數(shù) 不要求掛起線程。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            err = (*jvmti)->GetOwnedMonitorInfo(jvmti, thr, νm_monitors, &arr_monitors);

            printf("Number of Monitors returned : %d\n", num_monitors);

            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

使用 JVMTI GetThreadState 接口可以獲取線程的狀態(tài)信息。

線程狀態(tài)可以為以下值之一:

  • 線程已終止
  • 線程活動(dòng)
  • 線程可運(yùn)行
  • 線程休眠
  • 線程在等待通知
  • 線程處于對(duì)象等待狀態(tài)
  • 線程為本地狀態(tài)
  • 線程已掛起
  • 線程已中斷
<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            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;

            }
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->
使用 JVMTI 顯示 JVM 中的所有用戶線程

JVMTI 函數(shù) GetAllThreads 用于顯示 JVM 已知的所有活動(dòng)線程。這些線程是關(guān)聯(lián)到 VM 的 Java 編程語言線程。

以下代碼對(duì)此進(jìn)行了說明:

<!-- 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");

            }

            }

            }
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

這將在您的終端上產(chǎ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
            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->
獲取 JVM 線程堆棧跟蹤

JVMTI 接口 GetStackTrace 可用于獲取關(guān)于線程堆棧的信息。如果 max_count 小于堆棧的深度,最深框架的 max_count 數(shù)將返回,否則返回整個(gè)堆棧。調(diào)用此函數(shù)無需掛起線程。

下例產(chǎn)生至多 5 個(gè)最深框架。如果存在任何框架,則還將輸出當(dāng)前執(zhí)行的方法名。

<!-- 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);

            }

            }

            }

            }

            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

這將使您的終端產(chǎn)生以下輸出:

<!-- 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;
            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

使用 JVMTI 分析堆

本節(jié)介紹如何獲取關(guān)于使用堆的信息的示例代碼。例如,我們已經(jīng)按“代理初始化”一節(jié)中所述為 VM Object Allocation 事件進(jìn)行了注冊(cè)。當(dāng) JVM 分配了 Java 編程語言可見但其他工具機(jī)制不能探測(cè)到的對(duì)象時(shí),我們將得到通知。這一點(diǎn)與 JVMPI 截然不同,JVMPI 在分配任何對(duì)象時(shí)都將發(fā)送事件。在 JVMTI 中,針對(duì)用戶分配的對(duì)象不會(huì)發(fā)送任何事件,因?yàn)樗谕褂玫氖亲止?jié)碼工具。例如,在 SimpleThread.java 程序中,分配 MyThread 或 Thread 對(duì)象時(shí),我們是不會(huì)得到通知的。以后將單獨(dú)發(fā)表一篇文章,描寫如何使用字節(jié)碼工具獲取此信息。

VM Object Allocation 事件對(duì)于確定有關(guān)由 JVM 分配的對(duì)象的信息十分有用。在 Agent_OnLoad 方法中,我們將 callbackVMObjectAlloc 注冊(cè)為發(fā)送 VM Object Allocation 事件時(shí)調(diào)用的函數(shù)。回調(diào)函數(shù)參數(shù)包含關(guān)于已分配對(duì)象的信息,如對(duì)象類和對(duì)象大小的 JNI 本地參考。借助于 jclass 參數(shù) object_klass ,我們可以使用 GetClassSignature 函數(shù)獲取關(guān)于類名的信息。我們可以把下面給出的對(duì)象類及其大小打印出來。注意避免過多的輸出,我們僅需輸出超過 50 個(gè)字節(jié)的對(duì)象信息就行了。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            /* 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);

            }

            ...

            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

我們使用 上面 所介紹的 GetStackTrace 方法來輸出正在分配該對(duì)象的線程的堆棧跟蹤。我們依照該節(jié)所述獲取指定深度的 框架。這些框架將作為 jvmtiFrameInfo 結(jié)構(gòu)返回,這些結(jié)構(gòu)包含每個(gè)框架的 jmethodID (即 frames[x].method )。 GetMethodName 函數(shù)可以將 jmethodID 映射到特殊的方法名中。在此示例的最后部分,我們還將使用 GetMethodDeclaringClass 和 GetClassSignature 函數(shù)獲取從其中調(diào)用過此方法的類的名稱。

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            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);

            }

            }

            }

            }

            ...

            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

注意,完成任務(wù)時(shí)應(yīng)釋放由這些函數(shù)分配給 char 數(shù)組的內(nèi)存:

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
             err = (*jvmti)->Deallocate(jvmti, (void*)className);

            err = (*jvmti)->Deallocate(jvmti, (void*)methodName);

            err = (*jvmti)->Deallocate(jvmti, (void*)declaringClassName);

            ...

            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

此代碼的輸出如下所示:

<!-- 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;
            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

原始類的返回名稱是相應(yīng)原始類型的簽名字符類型。例如, java.lang.Integer.TYPE 為“I”。

在 VM Object Allocation 的回調(diào)方法中,我們?nèi)詫⑹褂? IterateOverObjectsReachableFromObject 函數(shù)演示如何獲取關(guān)于堆的附加信息。在此示例中,我們將 JNI 參考作為一個(gè)參數(shù)傳遞給剛剛分配的對(duì)象,該函數(shù)將在此新分配對(duì)象所能直接或間接到達(dá)的所有對(duì)象中迭代。對(duì)于每個(gè)可到達(dá)的對(duì)象,另外還有一個(gè)定義的回調(diào)函數(shù)可對(duì)其進(jìn)行描述。在此示例中,傳遞到 IterateOverObjectsReachableFromObject 的回調(diào)函數(shù)名為 reference_object

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            err = (*jvmti)->IterateOverObjectsReachableFromObject

            (jvmti, object, &reference_object, NULL);

            if ( err != JVMTI_ERROR_NONE ) {

            printf("Cannot iterate over reachable objects\n");

            }

            ...
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

reference_object 函數(shù)定義如下:

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            /* 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;

            }

            ...
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

在此示例中,我們使用 IterateOverObjectsReachableFromObject 函數(shù)計(jì)算新分配對(duì)象所能到達(dá)的所有對(duì)象的 總的大小,以及它們的對(duì)象類型。對(duì)象類型可以從 reference_kind 參數(shù)中確定。然后打印此信息以接收如下輸出:

<!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
            
              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.
            
          


<!-- END VCD7 CODE SAMPLE COMPONENT -->

注意,位于 JVMTI 中的類似迭代函數(shù)允許迭代的對(duì)象有:整個(gè)堆(可到達(dá)的和不可到達(dá)的);根目錄對(duì)象和根目錄對(duì)象所能直接或間接到達(dá)的所有對(duì)象;堆中是指定類的實(shí)例的所有對(duì)象。使用這些函數(shù)的技巧和前面所介紹的類似。在執(zhí)行這些函數(shù)期間,堆的狀態(tài)沒有任何變化:沒有分配任何對(duì)象,沒有對(duì)任何對(duì)象進(jìn)行垃圾收集,并且對(duì)象的狀態(tài)(包括堆值)也沒有任何變化。結(jié)果,執(zhí)行 Java 編程語言代碼的線程、嘗試恢復(fù)執(zhí)行 Java 編程語言代碼的線程和嘗試執(zhí)行 JNI 函數(shù)的線程都完全停了下來。所以,在對(duì)象參考回調(diào)函數(shù)中,不能使用任何 JNI 函數(shù);在沒有特別允許的情況下,也不允許使用任何 JVMTI 函數(shù)。

編譯和執(zhí)行示例代碼

要編譯并運(yùn)行這里描述的示例應(yīng)用程序的代碼,請(qǐng)按以下步驟操作:

  1. 設(shè)置 JDK_PATH 為指向 J2SE 1.5 發(fā)行版
    <!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
                    JDK_PATH="/home/xyz/j2sdk1.5.0/bin"
                  


    <!-- END VCD7 CODE SAMPLE COMPONENT -->
  2. 使用 C 語言編譯器構(gòu)建共享庫。我們使用的是 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 -->
  3. 要加載并運(yùn)行代理庫,請(qǐng)?jiān)?VM 啟動(dòng)過程中使用下面的命令行參數(shù)之一。
    <!-- BEGIN VCD7 CODE SAMPLE COMPONENT -->
                    -agentlib:<jvmti-agent-library-name>
    
                    -agentpath:/home/foo/jvmti/<jvmti-agent-library-name>
    
                    
                  


    <!-- END VCD7 CODE SAMPLE COMPONENT -->然后如下運(yùn)行示例 Java 應(yīng)用程序:
    <!-- 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 上構(gòu)建和測(cè)試的。

結(jié)束語

在本文中,我們演示了 JVMTI 提供用于監(jiān)控和管理 JVM 的一些接口。JVMTI 規(guī)范 (JSR-163) 旨在為需要訪問 VM 狀態(tài)的廣泛的工具提供一個(gè) VM 接口,這些工具包括但不限于:分析、調(diào)試、監(jiān)控、線程分析和覆蓋率分析工具。

建議開發(fā)人員不要使用 JVMPI 接口開發(fā)工具或調(diào)試實(shí)用工具,因?yàn)?JVMPI 是一種不受支持的實(shí)驗(yàn)技術(shù)。應(yīng)考慮使用 JVMTI 編寫 Java 虛擬機(jī)的所有分析和管理工具。

使用 Java 虛擬機(jī)工具接口(JVMTI)創(chuàng)建調(diào)試和分析代理


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦?。?!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 极品xxxx欧美一区二区 | 99SE久久爱五月天婷婷 | 欧美1024性视频 | 日韩国产成人资源精品视频 | 天天插天天舔 | 加勒比 テカ痴女の猛烈交尾 | 国产你懂得 | a级欧美片免费观看 | 国产精品成人在线 | 国产一区二区免费 | 日韩丝袜在线观看 | 免费国产黄频在线观看视频 | 欧美一级黄视频 | 魔法骑士在线观看免费完整版 | 97超级碰碰碰碰在线视频 | 色欲天天婬色婬香视频综合网 | 亚洲综合色丁香婷婷六月图片 | 久久精品23| 欧美精品一区三区 | 毛片基地免费视频a | 亚洲v日本v欧美v综合v | 草久网 | 毛片免费在线 | 99视频在线观看视频一区 | 人人艹逼 | 中国人免费的片 | 免费大香伊蕉在人线国产 | 精品伊人久久大线蕉地址 | 欧美日韩视频在线 | 国产男女免费完整版视频 | www.久久久.com | 欧美一区二区三区视频 | v片在线免费观看 | 精品国产自在2o18 | 黄色一级大片在线免费看产 | 色tv国产 | 亚洲精品性视频 | 中文字幕一区二区视频 | 日本黄视频在线观看 | 午夜精品影院 | 大色综合 |