目录
一、java agent介绍
二、Java Agent的原理
1、原理是什么?
2、实现agent启动方法
3、agent加载
4、启动时修改
5、运行时修改
一、java agent介绍
java agent本质上可以理解为一个插件,该插件就是一个精心提供的jar包,这个jar包通过JVMTI(JVM Tool Interface)完成加载,最终借助JPLISAgent(Java Programming Language Instrumentation Services Agent)完成对目标代码的修改。
java agent技术的主要功能如下:
- 可以在加载java文件之前做拦截把字节码做修改
- 可以在运行期将已经加载的类的字节码做变更
- 还有其他的一些小众的功能
- 获取所有已经被加载过的类
- 获取所有已经被初始化过了的类
- 获取某个对象的大小
- 将某个jar加入到bootstrapclasspath里作为高优先级被bootstrapClassloader加载
- 将某个jar加入到classpath里供AppClassloard去加载
- 设置某些native方法的前缀,主要在查找native方法的时候做规则匹配
二、Java Agent的原理
1、原理是什么?
Java Agent的原理主要是通过Java的Instrumentation API和JVMTI(Java Virtual Machine Tool Interface)来实现的。
Java Agent是一种运行在JVM中的特殊程序,可以在运行时修改或分析类的字节码。通过Instrumentation API,Java Agent可以在类加载到JVM之前或之后,对类的字节码进行修改或分析,以实现各种功能,如性能监控、故障排查、代码热替换等。
JVMTI是JVM提供的一组原生接口,用于监控和控制JVM的运行状态。Java Agent可以通过JVMTI获取JVM的运行信息,如线程状态、内存使用情况等,以实现更加精细的控制和监控。
总的来说,Java Agent的原理是通过Instrumentation API和JVMTI,在运行时对类的字节码进行修改或分析,以实现各种功能。
- 编写Java Agent代码:首先需要编写Java Agent的代码,实现所需的功能。Java Agent的代码通常包括一个premain方法,该方法会在目标JVM启动之前被执行,可以在其中进行字节码修改或分析等操作。
- 打包Java Agent:将Java Agent代码打包成一个独立的jar文件,以便后续使用。
- 使用Java Agent:在启动目标JVM时,通过添加-javaagent参数来指定Java Agent的路径和名称,如:java -javaagent:/path/to/agent.jar MyApp。这样,JVM在启动时就会加载并执行Java Agent的代码。
- 调试和测试:在实际使用中,需要对Java Agent进行调试和测试,以确保其能够正确地工作并实现预期的功能。
通过java agent技术进行类的字节码修改最主要使用的就是Java Instrumentation API。下面将介绍如何使用Java Instrumentation API进行字节码修改。
2、实现agent启动方法
Java Agent支持目标JVM启动时加载,也支持在目标JVM运行时加载,这两种不同的加载模式会使用不同的入口函数,如果需要在目标JVM启动的同时加载Agent,那么可以选择实现下面的方法:
[1] public static void premain(String agentArgs, Instrumentation inst);
[2] public static void premain(String agentArgs);
JVM将首先寻找[1],如果没有发现[1],再寻找[2]。如果希望在目标JVM运行时加载Agent,则需要实现下面的方法:
[1] public static void agentmain(String agentArgs, Instrumentation inst);
[2] public static void agentmain(String agentArgs);
这两组方法的第一个参数AgentArgs是随同 “–javaagent”一起传入的程序参数,如果这个字符串代表了多个参数,就需要自己解析这些参数。inst是Instrumentation类型的对象,是JVM自动传入的,我们可以拿这个参数进行类增强等操作。
3、agent加载
- 启动时加载
- 启动参数增加-javaagent:[path],其中path为对应的agent的jar包路径
- 运行中加载
- 使用com.sun.tools.attach.VirtualMachine加载
4、启动时修改
启动时修改主要是在jvm启动时,执行native函数的Agent_OnLoad方法,在方法执行时,执行如下步骤:
- 创建InstrumentationImpl对象
- 监听ClassFileLoadHook事件
- 调用InstrumentationImpl的loadClassAndCallPremain方法,在这个方法里会去调用javaagent里MANIFEST.MF里指定的Premain-Class类的premain方法
5、运行时修改
运行时修改主要是通过jvm的attach机制来请求目标jvm加载对应的agent,执行native函数的Agent_OnAttach方法,在方法执行时,执行如下步骤:
- 创建InstrumentationImpl对象
- 监听ClassFileLoadHook事件
- 调用InstrumentationImpl的loadClassAndCallAgentmain方法,在这个方法里会去调用javaagent里MANIFEST.MF里指定的Agentmain-Class类的agentmain方法