java之agent
java之agent
我们平时用的很多工具,都是基于Java Agent实现的,例如常见的热部署JRebel,各种线上诊断工具(btrace, greys), APM , eg new relic, Profiling Yourkit etc..
Java Agent也是一个jar,只是启动方式和普通Jar包有所不同,对于普通的Jar包,通过指定类的main函数进行启动,但是Java Agent并不能单独启动,必须依附在一个Java应用程序运行
java agent技术的主要功能如下:
- 可以在加载java文件之前做拦截把字节码做修改
- 可以在运行期将已经加载的类的字节码做变更
通过java agent技术进行类的字节码修改最主要使用的就是Java Instrumentation API。
启动加载
在目标JVM运行时加载
[1] public static void premain(String agentArgs, Instrumentation inst); |
启动参数增加-javaagent:[path],其中path为对应的agent的jar包路径
- 创建InstrumentationImpl对象
- 监听ClassFileLoadHook事件
- 调用InstrumentationImpl的loadClassAndCallPremain方法,在这个方法里会去调用javaagent里MANIFEST.MF里指定的Premain-Class类的premain方法
运行时加载
[1] public static void agentmain(String agentArgs, Instrumentation inst); |
使用com.sun.tools.attach.VirtualMachine加载
try { |
运行时修改主要是通过jvm的attach机制来请求目标jvm加载对应的agent
- 创建InstrumentationImpl对象
- 监听ClassFileLoadHook事件
- 调用InstrumentationImpl的loadClassAndCallAgentmain方法,在这个方法里会去调用javaagent里MANIFEST.MF里指定的Agentmain-Class类的agentmain方法
Instrumentation
https://www.baeldung.com/java-instrumentation
java.lang.instrument.Instrumentation
- addTransformer – adds a transformer to the instrumentation engine
- getAllLoadedClasses – returns an array of all classes currently loaded by the JVM
- retransformClasses – facilitates the instrumentation of already loaded classes by adding byte-code
- removeTransformer – unregisters the supplied transformer
- redefineClasses – redefine the supplied set of classes using the supplied class files, meaning that the class will be fully replaced, not modified as with retransformClasses
instrument是JVM提供的一个可以修改已加载类的类库,专门为Java语言编写的插桩服务提供支持。
redefineClasses和retransformClasses。一个是重新定义class,一个是修改class。
可使用ASM或Javassist对传入的字节码进行改写或替换
案例
https://mp.weixin.qq.com/s/ArP0CtVZMB2oUYSdjr7RGw
通过 asm 在方法的执行入口和执行出口处,植入几行记录时间戳的代码,当方法结束后,通过时间戳来获取方法的耗时。
public static void premain(String args, Instrumentation instrumentation) { |
我们通过 instrumentation.addTransformer 注册一个转换器,转换器重写了 transform 方法,方法入参中的 classfileBuffer 表示的是原始的字节码,方法返回值表示的是真正要进行加载的字节码。
远程采集已经处于运行中的 Java 进程的方法调用信息。
- agent 对指定类的方法进行字节码的修改,采集方法的入参和返回值。并通过 socket 将请求和返回发送到服务端
- 服务端通过 attach api 访问运行中的 Java 进程,并加载 agent ,使 agent 程序能对目标进程生效
- 服务端加载 agent 时指定需要采集的类和方法
- 服务端开启一个端口,接受目标进程的请求信息
summary
java agent is just a specially crafted jar file. It utilizes the Instrumentation API that the JVM provides to alter existing byte-code that is loaded in a JVM.
For an agent to work, we need to define two methods:
- premain – will statically load the agent using -javaagent parameter at JVM startup
- agentmain – will dynamically load the agent into the JVM using the Java Attach API
Misc
Java 调试、热部署、JVM 背后的都是通过 Java Agent
-各个 Java IDE 的调试功能,例如 eclipse、IntelliJ ; -热部署功能,例如 JRebel、XRebel、 spring-loaded; -各种线上诊断工具,例如 Btrace、Greys,还有阿里的 Arthas; -各种性能分析工具,例如 Visual VM、JConsole 等;
例如: JMX
用了 management-agent.jar 这个Java Agent 来实现的
如果服务允许远程查看JVM 信息:
-Dcom.sun.management.jmxremote |
management-agent.jar 包下MANIFEST.MF
Manifest-Version: 1.0 |
https://mp.weixin.qq.com/s/nFoxGH7mGt20P0e5rcZivQ
https://mp.weixin.qq.com/s/_JqxUmQKvUpUulmcSMQq2A
https://zhuanlan.zhihu.com/p/101828645