JVMTI 之 HelloWorld

红薯 发布于 2010/06/17 22:43
阅读 1K+
收藏 3

看了一些关于JVMTI的资料,对JVMTI的工作原理有了一个基本的了解,从SUN给出的文档来看,JVMTI将是JVMPIJVMDI等技术的替代品,并且建议用户使用JVMTI,而不是JVMPIJVMDI,因为在以后的JDK版本中,后两种技术可能将不再支持了。我们希望通过一个最简单的HelloWorld程序来讲述一下JVMTI编程的一些基本方式。

1.原理和目标。我们的目标就是 通过JVMTI技术来监控JVM的一些行为,具体到我们的这个例子中就是想监控JVM中抛出的异常(Exception)事件。为实现这一目标,我们需要编写一个Agent动态库,在JVM启动的时候将该动态库加载进去,在动态库中需要对异常(Exception)事件进行注册,使得该类事件发生时能触发我们编写的函数,进行相应的处 理。所以我们例子中涉及到两个部分,一部分是Java编写的HelloWorld程序,该程序中会抛出一个异常事件;另一部分是C语言编写的Agent动态库,用来监控JVM的行为。

2HelloWorld程序。这是一个很简单的java 程序,在main函 数中打印一条"HelloWorld"字符 串,并抛除一个Exception异常。如 下:

//Sample.java:

class Sample{

public static void main    (String[] orgs){                         

            System.out.println("hello,world");                                   

            String str =null;

            try{

                        str.toString();

            }catch(Exception e)

            {

                        System.out.println("In Java: Got an exception in java code");

            }

}         

}

3Agent动态库。Agent动态库在window下为dll文件,在Linux/Unixso文件,其编写过程非常简单:

首先,在代码文件中加入”jvmti.h”头文件,该文件包含在{JDK1.5主目录}\inlcude目录下,它包含了我们所需要用到的一些变量和函数的定义。如果编译时找不 到该头文件,则应该将“{JDK1.5主目录}\inlcude”和“{JDK1.5主目录}\inlcude\win32”加入到头文件的搜索路径中。

其次,实现Agent_OnLoad()Agent_OnUnload()方法。Agent_Onload()方法在Agent被加载时调用,我们需要在该函数中完成变量的初始化,监控事件的注册等操 作,从而使得当某类事件发生时,JVM能够通 知该AgentAgent_Unload()方法在Agent被卸载时调用,用来释放之前申请的内存空间,防止内存泄漏。我们首先需要 创建一个callbackException()函数,并通过SetEventCallbacks()方法将将该函数进行注册,使得当捕获到Exception事件时,能自动回调该函数:

callbacks.Exception = &callbackException;

            error = gb_jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));

            另外,我们需要通过SetEventNotificationMode()方法来向JVM预定Exception事件的通知,让JVM知道Agent对该类事件感兴趣:

            error= gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_EXCEPTION, (jthread)NULL);

这样,当JVM运行过程发生Exception事件时,callbackException()函数就会被调用,我们可以在该函数中完成自己的一些操作。在callbackException()函数中,我们会判断捕获到的Exception是从哪个方法中抛出的,如果是“main”方法的话,就打印出一行字符串:

printf("In Agent: Got an exception from Method: %s\n" ,name );

下面是Agent动态库的代码:

//MyAgent.cpp:

 

#include

#include

#include

#include "jvmti.h"

 

static jvmtiEnv *gb_jvmti = NULL;

static jvmtiCapabilities gb_capa;

static jrawMonitorID gb_lock;

 

static void          enter_critical_section(jvmtiEnv *jvmti)

{

    jvmtiError error;

    error = jvmti->RawMonitorEnter(gb_lock);

}

 

static void          exit_critical_section(jvmtiEnv *jvmti)

{

    jvmtiError error;

    error = jvmti->RawMonitorExit(gb_lock);

}

 

static void JNICALL callbackException(jvmtiEnv *jvmti_env, JNIEnv* env, jthread thr, jmethodID method, jlocation location, jobject exception, jmethodID catch_method, jlocation catch_location)

{

            enter_critical_section(gb_jvmti);

            {

                        char *name,*sig,*gsig;

                        jvmtiError  error  = gb_jvmti->GetMethodName(method, &name, &sig, &gsig);

                        if (error  != JVMTI_ERROR_NONE)

                        {

          printf("ERROR:GetMethodName!\n");

          return;

                        }

                        if(strcmp(name,"main")==0)

                        {

                                    printf("In Agent: Got an exception from Method: %s\n" ,name );

                        }

            }

            exit_critical_section(gb_jvmti);

}

 

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)

{

            jvmtiError error;

            jvmtiEventCallbacks callbacks;

 

            jint result = jvm->GetEnv((void **) &gb_jvmti, JVMTI_VERSION_1_0);

            if(result != JNI_OK || gb_jvmti==NULL)

            {

                        printf("ERROR: Unable to access JVMTI!");

                        return JNI_ERR;

            }

 

            memset(&gb_capa,0,sizeof(jvmtiCapabilities));

    gb_capa.can_signal_thread = 1;

    gb_capa.can_get_owned_monitor_info = 1;

    gb_capa.can_generate_method_entry_events = 1;

    gb_capa.can_generate_exception_events = 1;

    gb_capa.can_generate_vm_object_alloc_events = 1;

    gb_capa.can_tag_objects = 1; 

 

    error = gb_jvmti->AddCapabilities(&gb_capa);

            if(error != JVMTI_ERROR_NONE)

            {

                        printf("ERROR: Can't get JVMTI capabilities");

                        return JNI_ERR;

            }

           

            memset(&callbacks,0,sizeof(jvmtiEventCallbacks));

            callbacks.Exception = &callbackException;

 

            error = gb_jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));

            if(error != JVMTI_ERROR_NONE)

            {

                        printf("ERROR: Can't set jvmti callback!");

                        return JNI_ERR;

            }

 

            error = gb_jvmti->CreateRawMonitor("agent data", &gb_lock);

            if(error != JVMTI_ERROR_NONE)

            {

                        printf("ERROR: Can't create raw monitor!");

                        return JNI_ERR;

            }

 

 //   error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_VM_INIT, (jthread)NULL);

    error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_EXCEPTION, (jthread)NULL);

 

            return JNI_OK;

}

 

JNIEXPORT void JNICALL        Agent_OnUnload(JavaVM *vm)

{

            //

}

 

4.编译执行。我是用VC6来编译Agent动态库的,在VC6种创建一个Win32动态连接库项目,取名为MyAgentDll,然后将MyAgent.cpp文件加入该项目。选择“工具->选择->目录”,然后将“{JDK1.5主目录}\inlcude”和“{JDK1.5主目录}\inlcude\win32加入到“inlcude files”目录中。然后编译构建MyAgentDll.dll即可。

            因为JVMTIJDK1.5中加入的一项新功能,所以请先确认你所使用的JDK版本:

            #java -version

编译Sample.java。进入Sample所在目录,在命令行执行如下命令:

            # javac Sample.java

            执行Sample。将MyAgentDll.dll拷贝到Sample.java所在目录,用如下命令执行:

            # java -agentpath:e:\work\jvmit\MyAgentDll.dll Sample

            如果一切顺利的话,应该看到如下结果:

http://trema.blogbus.com/files/1136537674.jpg

            其中,“In Agent: Got an exception from Method: main”是Agent打 印出来的,其他两行是Sample.java打 印出来的。

5.结束语。我们上面描述的只是一个非常简单的JVMTI编程的例子,也是在别人代码的基础上修修改改得到的,它基本上说明了JVMTI编程的大致框架,如果在其基础上进行一些扩充和丰富,就可以做出一些很 有用的小工具。目前一些非常著名的程序性能分析工具,如JProbe, JRockit, Optimizeit等,虽然不知道其具体的技术细节,但猜想应该也是采用了类似的实现机制。

加载中
0
DustinYoung
DustinYoung

真巧,今天刚看到一些关于JVMTI的东西~~

0
午夜谨

你好,有个问题想咨询你。

我想要利用jvmti来获取一个java程序中一个目标类(比如student类)的所有对象。获取之后,要把这些对象按照student类的定义形式保存到文本文档中。
我先用FollowReferences给所有的student类的对象打上标签tag,然后用GetObjectsWithTags取出所有被打上标签的对象。现在,我已经可以获取所有打上标签的对象。但我还需要把这些对象按照student类的定义的形式,存储到本地文本文件中。但是,我不知道如何去按照student类的形式存储。所以,求高手帮忙指点一下。

      为了防止说不清,我举个简单的小例子:
      class student  //java程序中student类的定义,有两个值域,ID和name
      {
              int  ID;
              String name;
      }
      在文本文档中,保存的student对象:
      ID  (I) :  101
      name  (L)  :   Jack

返回顶部
顶部