首页  

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失败处理

大数据风控概述