Java千百问_08JDK详解(014)_如何编写JVMTI agent程序
2016-07-02 10:05
543 查看
点击进入_更多_Java千百问
了解JVMTI看这里:JVMTI是什么
我们需要使用C++编写agent程序,JVM在不同时机回调下面的接口函数:
其中第一个是jvm启动时调用,第二个是jvm运行时发出attach时调用,第三个是jvm卸载时调用。
其中*jvm参数传入JavaVM指针,可以用来操作JVM;*options参数是命令行传入的参数;*reserved是一个预留参数。
给出运行加载模式实现的一个简单功能:打印jvm内所有已经装载成功的class。具体如下:
我们将它命名为agent.cpp,下一步就是将它编译为动态链接库,不同操作系统的命令并不相同,这里以mac系统为例,具体如下:
由于是mac系统,所以引入了${JAVA_HOME}/include/darwin,linux和windows对应的应该是/include/linux、\include\win32。
执行后,我们会看到生成了了libagent.so文件。
死循环的目的在于不能让它结束,方便我们使用JVMTI agent进行操作。运行后结果如下:
JVMTI agent Test start
这时我们需要通过AttachAPI为运行中的JVM加载我们的agent,我们首先获取到目标JVM的pid(通过进程监控获取),然后指定agent动态资源库的路径,具体如下:
了解更多 AttachAPI看这里:[AttachAPI是什么][4]
这里我们需要引入${JAVA_HOME}/lib/tools.jar包,引入com.sun.tools.attach包。运行后,在目标JVM的Console中出现如下结果:
JVMTI agent Test start
cls sig=Ljava/lang/ClassLoader$NativeLibrary;
cls sig=Ljava/util/concurrent/ConcurrentMap;
cls sig=Ljava/lang/Error;
cls sig=[Ljava/lang/Error;
cls sig=Ljava/util/Set;
cls sig=Ljava/util/WeakHashMap;
cls sig=Ljava/lang/ref/Reference;
cls sig=[Ljava/lang/ref/Reference;
cls sig=Ljava/lang/StackOverflowError;
cls sig=Ljava/io/Serializable;
….
成功打印出了所有已经装载成功的class。
顺便提一句,如果采用启动加载模式,则需要在运行TestMain时加上如下参数:
注意,这里传入了*options参数:opt1,不过,我们的agent并没有使用他。
了解更多JVMTI的功能看这里:[JVMTI提供哪些功能][5]
1、如何编写JVMTI agent程序
了解JPDA看这里:JPDA是什么了解JVMTI看这里:JVMTI是什么
我们需要使用C++编写agent程序,JVM在不同时机回调下面的接口函数:
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved); JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *jvm)
其中第一个是jvm启动时调用,第二个是jvm运行时发出attach时调用,第三个是jvm卸载时调用。
其中*jvm参数传入JavaVM指针,可以用来操作JVM;*options参数是命令行传入的参数;*reserved是一个预留参数。
给出运行加载模式实现的一个简单功能:打印jvm内所有已经装载成功的class。具体如下:
/* * JVMTI agent * * Created on: 2016-07-02 * Author: sunjie */ #include <jvmti.h> #include <string> #include <cstring> #include <iostream> #include <list> #include <map> #include <set> #include <stdlib.h> #include <jni_md.h> JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { jvmtiEnv *jvmti; jint result = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1); if (result != JNI_OK) { printf("ERROR: Unable to access JVMTI!\n"); } jvmtiError err = (jvmtiError) 0; jclass *classes; jint count; err = jvmti->GetLoadedClasses(&count, &classes);//获取class if (err) { printf("ERROR: JVMTI GetLoadedClasses failed!\n"); } for (int i = 0; i < count; i++) { char *sig; jvmti->GetClassSignature(classes[i], &sig, NULL);//获取并打印class签名 printf("cls sig=%s\n", sig); } return err; } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) { // nothing to do }
我们将它命名为agent.cpp,下一步就是将它编译为动态链接库,不同操作系统的命令并不相同,这里以mac系统为例,具体如下:
g++ -I${JAVA_HOME}/include/ -I${JAVA_HOME}/include/darwin Agent.cpp -fPIC -shared -o libagent.so
由于是mac系统,所以引入了${JAVA_HOME}/include/darwin,linux和windows对应的应该是/include/linux、\include\win32。
执行后,我们会看到生成了了libagent.so文件。
2、如何测试agent程序
我们已经有了agent,下面我们测试一下。测试需要有一个目标JVM,所以我们简单模拟一个,具体如下:public class TestMain { public static void main(String[] args) throws InterruptedException { System.out.println("JVMTI agent Test start"); int i = 0; while (true) { Thread.sleep(1000); i++; } } }
死循环的目的在于不能让它结束,方便我们使用JVMTI agent进行操作。运行后结果如下:
JVMTI agent Test start
这时我们需要通过AttachAPI为运行中的JVM加载我们的agent,我们首先获取到目标JVM的pid(通过进程监控获取),然后指定agent动态资源库的路径,具体如下:
了解更多 AttachAPI看这里:[AttachAPI是什么][4]
public class TestAgent { public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException { String pid = "831"; // java进程pid String agentPath = "/Users/sunjie/Desktop/libagent.so"; // agent.so的路径 String options = null;// 传入agent的参数 VirtualMachine virtualMachine = com.sun.tools.attach.VirtualMachine.attach(pid); virtualMachine.loadAgentPath(agentPath, options); virtualMachine.detach(); } }
这里我们需要引入${JAVA_HOME}/lib/tools.jar包,引入com.sun.tools.attach包。运行后,在目标JVM的Console中出现如下结果:
JVMTI agent Test start
cls sig=Ljava/lang/ClassLoader$NativeLibrary;
cls sig=Ljava/util/concurrent/ConcurrentMap;
cls sig=Ljava/lang/Error;
cls sig=[Ljava/lang/Error;
cls sig=Ljava/util/Set;
cls sig=Ljava/util/WeakHashMap;
cls sig=Ljava/lang/ref/Reference;
cls sig=[Ljava/lang/ref/Reference;
cls sig=Ljava/lang/StackOverflowError;
cls sig=Ljava/io/Serializable;
….
成功打印出了所有已经装载成功的class。
顺便提一句,如果采用启动加载模式,则需要在运行TestMain时加上如下参数:
java -agentlib:Agent=opt1 TestMain
注意,这里传入了*options参数:opt1,不过,我们的agent并没有使用他。
了解更多JVMTI的功能看这里:[JVMTI提供哪些功能][5]
相关文章推荐
- 快速暴力解决Eclipse ADT和Android Studio兼容问题,创建同时兼容ADT和AS的安卓工程
- java集合框架
- 反射基础
- SpringBoot使用JPA操作数据库
- 在MySql中如何定义像Java中类型的Boolean类型
- HeadFirstJava——1_基本概念
- struts2 登录拦截
- JavaSE复习_11 IO流复习
- 第3章 Struts2框架--1、Struts2环境搭建
- Struts2下多文件的上传与下载
- Java集合源码分析(二)ArrayList
- eclipse的启动失败提示"发生了错误,请参阅日志文件"
- maven整合SSH(一)--struts2篇
- 从Eclipse到AndroidStudio(五)迁移一个eclipse工程到AndroidStudio有哪些坑
- Spring的传播行为与隔离级别,你必须懂的
- 从Eclipse到AndroidStudio(四)Gradle基本配置
- 从Eclipse到AndroidStudio(三)配置成你熟悉的操作
- 给Eclipse提速的7个技巧
- Java抽象类和接口
- 等额本金、等额本息工具类(Java版)