打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
JVMTI之HelloWorld - Trema & Colon - 博客大巴
JVMTI之HelloWorld
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://trema.blogbus.com/logs/1795689.html
JVMTI之HelloWorld
看了一些关于JVMTI的资料,对JVMTI的工作原理有了一个基本的了解,从SUN给出的文档来看,JVMTI将是JVMPI和JVMDI等技术的替代品,并且建议用户使用JVMTI,而不是JVMPI或JVMDI,因为在以后的JDK版本中,后两种技术可能将不再支持了。我们希望通过一个最简单的HelloWorld程序来讲述一下JVMTI编程的一些基本方式。
1.原理和目标。我们的目标就是通过JVMTI技术来监控JVM的一些行为,具体到我们的这个例子中就是想监控JVM中抛出的异常(Exception)事件。为实现这一目标,我们需要编写一个Agent动态库,在JVM启动的时候将该动态库加载进去,在动态库中需要对异常(Exception)事件进行注册,使得该类事件发生时能触发我们编写的函数,进行相应的处理。所以我们例子中涉及到两个部分,一部分是Java编写的HelloWorld程序,该程序中会抛出一个异常事件;另一部分是C语言编写的Agent动态库,用来监控JVM的行为。
2.HelloWorld程序。这是一个很简单的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");
}
}
}
 
3.Agent动态库。Agent动态库在window下为dll文件,在Linux/Unix为so文件,其编写过程非常简单:
首先,在代码文件中加入”jvmti.h”头文件,该文件包含在{JDK1.5主目录}\inlcude目录下,它包含了我们所需要用到的一些变量和函数的定义。如果编译时找不到该头文件,则应该将“{JDK1.5主目录}\inlcude”和“{JDK1.5主目录}\inlcude\win32”加入到头文件的搜索路径中。
其次,实现Agent_OnLoad()和Agent_OnUnload()方法。Agent_Onload()方法在Agent被加载时调用,我们需要在该函数中完成变量的初始化,监控事件的注册等操作,从而使得当某类事件发生时,JVM能够通知该Agent。Agent_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即可。
因为JVMTI是JDK1.5中加入的一项新功能,所以请先确认你所使用的JDK版本:
#java -version
编译Sample.java。进入Sample所在目录,在命令行执行如下命令:
# javac Sample.java
执行Sample。将MyAgentDll.dll拷贝到Sample.java所在目录,用如下命令执行:
# java -agentpath:e:\work\jvmit\MyAgentDll.dll Sample
如果一切顺利的话,应该看到如下结果:
其中,“In Agent: Got an exception from Method: main”是Agent打印出来的,其他两行是Sample.java打印出来的。
5.结束语。我们上面描述的只是一个非常简单的JVMTI编程的例子,也是在别人代码的基础上修修改改得到的,它基本上说明了JVMTI编程的大致框架,如果在其基础上进行一些扩充和丰富,就可以做出一些很有用的小工具。目前一些非常著名的程序性能分析工具,如JProbe, JRockit, Optimizeit等,虽然不知道其具体的技术细节,但猜想应该也是采用了类似的实现机制。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
第一次尝试jvmti - Agile - 博客大巴
pinpoint字节码增强技术原理
打印3次HelloWorldC语言代码
回调函数
Chromium多线程通信的Closure机制分析
RabbitMQ tutorial - _Hello world!_
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服