javaagent两种加载方式及实例
所属分类 javaagent
浏览量 3135
两种获取Instrumentation接口实例的方法
agent on load premain
agent on attach agentmain
关注 JVMTI 事件 ClassFileLoadHook 事件
读取字节码文件之后回调 , 可对字节码进行重定义或重转换
premain 与 agentmain 的区别
premain和agentmain 都是为了回调Instrumentation实例并激活sun.instrument.InstrumentationImpl#transform()
从而回调注册到Instrumentation中的ClassFileTransformer实现字节码修改,功能基本一样。
两者主要区别
premain 是JDK1.5引入的,agentmain 是JDK1.6引入的
premain需要通过命令行使用外部代理jar包,即-javaagent:代理jar包路径;
agentmain 通过attach机制直接附着到目标VM中加载代理
premain 作用于JVM加载的所有类,所有类首次加载并且进入程序main()方法之前,premain方法会被激活,然后所有被加载的类都会执行ClassFileTransformer列表中的回调。
agentmain 采用attach机制,需要借助Instrumentation#retransformClasses(Class< ?>... classes)让对应的类重新转换,激活重新转换的类执行ClassFileTransformer列表中的回调。
premain 方式
public static void premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);
带Instrumentation参数的优先级高,会被优先调用
agentArgs 通过命令行参数传入
MANIFEST.MF 文件,必须包含 Premain-Class 选项,通常也会加入Can-Redefine-Classes 和 Can-Retransform-Classes 选项
使用参数 -javaagent: jar包路径启动代理
premain加载过程
1.创建并初始化 JPLISAgent
2.读取 MANIFEST.MF 文件参数,设置 JPLISAgent 参数
3.监听 VMInit 事件,在 JVM 初始化完成后
A 创建 InstrumentationImpl 对象
B 监听 ClassFileLoadHook 事件
C 调用 InstrumentationImpl 的loadClassAndCallPremain方法,在该方法里调用 javaagent 中 MANIFEST.MF 里指定的Premain-Class 类的 premain 方法
agentmain 方式
public static void agentmain(String agentArgs, Instrumentation inst);
public static void agentmain(String agentArgs);
// 列出所有VM实例
List list = VirtualMachine.list();
// attach目标VM
VirtualMachine.attach(descriptor.id());
// 目标VM加载Agent
VirtualMachine#loadAgent("代理Jar路径","命令参数");
agentmain 加载过程
1 创建并初始化JPLISAgent
2 解析MANIFEST.MF 里的参数 ,设置 JPLISAgent
3.监听 VMInit 事件,在 JVM 初始化完成之后
(1)创建 InstrumentationImpl 对象
(2)监听 ClassFileLoadHook 事件
(3)调用 InstrumentationImpl 的loadClassAndCallAgentmain方法,在该方法里调用javaagent里 MANIFEST.MF 里指定的Agent-Class类的agentmain方法。
Instrumentation 的一些限制
大多数情况下,使用Instrumentation都是使用其字节码插桩的功能
premain和agentmain两种方式修改字节码的时机都是类文件加载之后,必须要带有Class类型的参数,不能通过字节码文件和自定义的类名重新定义一个本来不存在的类。
类重定义Instrumentation#retransformClasses()方法,有以下限制
1.新类和老类的父类必须相同;
2.新类和老类实现的接口数也要相同,并且是相同的接口;
3.新类和老类访问符必须一致。 新类和老类字段数和字段名要一致;
4.新类和老类新增或删除的方法必须是private static/final修饰的;
5.可以删除修改方法体。
重新定义一全新类 可以创建新的类加载器去加载
MANIFEST.MF 文件 参数在 pom文件里指定
maven-jar-plugin manifestEntries
premain pom
https://gitee.com/dyyx/demos/blob/master/jvmtools/pom.xml
mainagent pom
https://gitee.com/dyyx/demos/blob/master/jvmtools/pom-main-agent.xml
premain agent 打包
mvn clean source:jar install -Dmaven.test.skip
main agent 打包
mvn clean source:jar install -Dmaven.test.skip -f pom-main-agent.xml
java -javaagent:jvmtools-1.0.jar -cp .:jvmtools-1.0.jar dyyx.agent.premain.PreMainTest
java -javaagent:jvmtools-1.0.jar=premain__args -cp .:jvmtools-1.0.jar dyyx.agent.premain.PreMainTest
java -cp .:jvmtools-1.0.jar dyyx.agent.ClientMain list
Caused by: java.lang.ClassNotFoundException: com.sun.tools.attach.VirtualMachine
java -cp .:jvmtools-1.0.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/lib/tools.jar dyyx.agent.ClientMain list
java -cp .:jvmtools-1.0.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/lib/tools.jar dyyx.agent.ClientMain attach 92108 /Users/dugang/tmp/jvmtools-1.0.jar
agent jar包需要使用绝对路径,否则会加载不到
Error opening zip file or JAR manifest missing: jvmtools-1.0.jar
两种agent例子代码
https://gitee.com/dyyx/demos/tree/master/jvmtools/src/main/java/dyyx/agent
上一篇
下一篇
银华日利(511880) vs 华宝添益(511990)
java debug 原理简介
javaagent机制
Java8日期api使用
jconsole连接jmx失败处理
大数据风控概述