Java Agent
agent是Java提供的代理插件技术,可以动态加载进目标JVM内部,收集对应应用的信息,进行字节码修改增强技术等。
Agent实现技术JVMTI
JVM提供了一套Native接口Java Virtual Machine Tool Interface(JVMTI),可以让外部程序访问JVM内部,完成虚拟机交互相关功能。通过当前接口编写agent有两种方式。
- 原生JVMTI的Native直接访问,编写agent需要编译打包成动态链接库.dll/.so文件。
- Instumentation API是Java提供的JVM相关操作的API,也是通过JVMTI实现和虚拟机交互的,编写的agent需要打包成jar加载。
Agent注入目标JVM的方式
- 目标应用启动时通过java启动参数附加。
#java -javaagent:[agent.jar/agent.so][=param] -jar [目标jar]
java -javaagent:./attachAgent.jar -jar Hello.jar
- 目标应用正在运行,通过JVM Tool提供的attach机制动态附加。
//attach pid
VirtualMachine vm = VirtualMachine.attach(pid);
try {
//load agent
vm.loadAgent("./agent.jar");
}finally {
//detach 解除attach
vm.detach();
}
JavaAgent编写
- 编写agent入口类,根据使用附加方式不同,实现方法不同
//启动附加实现,main方法之前执行
premain(String agentArgs, Instrumentation inst);
premain(String agentArgs);
//attach动态附加实现,附加完成直接执行
gentmain(String agentArgs, Instrumentation inst);
agentmain(String agentArgs);
- 编写打包指定程序入口配置MANIFEST.MF,也可以通过pom文件进行配置打包
Manifest-Version: 1.0
Agent-Class: agent.AttachAgent
Premain-Class: agent.AttachAgent
//允许重新定义已加载的类
Can-Redine-Classes: true
//允许修改已加载未初始化的类
Can-Retransform-Classes: true
- 内部字节码操作,通过Instrumentation.addTransformer()方法注册字节码操作ClassFileTransformer 事件,实现ClassFileTransformer.transform()方法,编写操作字节码的相关逻辑,如果class已经加载进jvm,可以执行retransformClasses重新触发transform拦截事件。
//实现ClassFileTransformer接口的addTransformer方法
public static void agentmain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//对jvm加载类进行重新处理,字节码操作可用javassit、ASM
return ClassFileTransformer.super.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
}
});
//获取已加载的类
Class[] classs = inst.getAllLoadedClasses();
for (Class cl : classs) {
//重新加载,并执行transform事件
inst.retransformClasses(cl);
}
}
- Instrumentation部分方法
//注册transform事件
void addTransformer(ClassFileTransformer transformer);
//重新加载尚未初始化或实例化的类,执行Transform事件
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
//对已经加载并且初始化的类重新定义
void redefineClasses(ClassDefinition... definitions)throws ClassNotFoundException, UnmodifiableClassException;
//获取所有已加载类
Class[] getAllLoadedClasses();
应用场景
应用诊断:Arthas
代码调试:Debug