一、基本概念
Java agent
是在JDK1.5
引入的,是一种可以动态修改Java
字节码的技术。java类编译之后形成字节码被JVM
执行,JVM
在执行这些字节码之前获取这些字节码信息,并且对这些字节码进行修改,来完成一些额外的功能,这种就是java agent
技术。
二、适用场景
-
java agent
能够在加载java
字节码之前进行拦截并对字节码进行修改 -
在
jvm
运行期间修改已经加载的字节码
通过以上两种就可以实现在一些框架或是技术的采集点进行字节码修改,可以对应用进行监控,或是对执行指定方法或是接口时额外添加操作(打印日志、打印方法执行时间、采集方法的入参和结果等)
三、实现原理
了解java agent
的实现原理就必须先了解java
的类加载机制(还不了解的自行了解),这个是了解java agent
的前提。
其次需要了解的是JVMTI
以及JVMTIAgent
,下面分别介绍下
3.1、JVMTI
JVMTI
是JVM Tool Interface
的缩写,是JVM
暴露出来给用户扩展使用的接口集合,JVMTI
是基于事件驱动的,JVM
每执行一定的逻辑就会调用一些事件的回调接口,这些接口可以给用户自行扩展来实现自己的逻辑
3.2、JVMTIAgent
JVMTIAgent
是一个动态库,利用JVMTI
暴露出来的接口实现用户自行的逻辑(eclipse
、idea
等工具等代码调试就是通过这个实现的)
JVMTIAgent
主要有三个方法:
Agent_OnLoad
方法,如果agent
在启动时加载,就执行这个方法
Agent_OnAttach
方法,如果agent
不是在启动的时候加载的,是我们先attach
到目标线程上,然后对对应的目标进程发送load
命令来加载agent
,在加载过程中调用Agent_OnAttach
函数Agent_OnUnload
方法,在agent
做卸载掉时候调用
3.3、instrument agent
instrument agent
实现了Agent_OnLoad
方法和Agent_OnAttach
方法,也就是即能在启动的时候加载agent
,也可以在运行期来加动态加载agent
,运行期动态加载agent
依赖JVM
的attach
机制实现,通过发送load
命令来加载agent
3.4、JVM Attach机制
jvm attach
机制上JVM
提供的一种JVM
进程间通信的功能,能让一个进程传命令给另一个进程,并进行一些内部的操作,比如进行线程dump
,那么就需要执行jstack
进行,然后把pid
等参数传递给需要dump
的线程来执行,这就是一种java attach
。
3.5、Class Transform的实现
第一次类加载的时候要求被transform
的场景,在加载类文件的时候发出ClassFileLoad
事件,交给instrument agent
来调用java agent
里注册的ClassFileTransformer
实现字节码的修改
3.6、Class Redefind的实现
类重新定义,主要用在已经被加载的类上
四、实现字节码增强的技术框架有哪些?
java agent
实现字节码增强到过程:
1.修改字节码、2.加载新字节码、3.替换旧字节码
- Java 动态代理
Java Proxy API
通过invoke
方法拦截出来相应的代码逻辑。Proxy
是面向接口的,被代理的Class
的所有方法调用都会通过反射调用invoke
方法。
缺点:性能开销大
-
Java 5 提供的Instrumentation API
适应场景:适用于监控
缺点:不适合处理灵活的代码逻辑
Instrumentation API
不仅可以做字节码增强,还可以通过调用getObjectSize(Object o)
方法来计算一个对象的精确大小。
- ASM
ASM
是一个提供字节码解析和操作的框架。CGlib
框架是基于ASM
实现,而常用的框架Hibernate、Spring
是基于CGlib
实现AOP
的。
ASM 对使用者屏蔽了整个类的字节码的长度、偏移量,能够灵活、方便地解析和操作字节码。主要提供Core API
和Tree API
。
Core API 主要的类(接口):
ClassVisitor、ClassAdapter、ClassReader、ClassWriter
Tree API 主要的类(接口):
工具类:
TraceClassVisitor、CheckClassAdapter、ASMifier、Type
TraceClassVisitor 能打印ClassWriter 提供的byte[] 字节数组。
TraceClassVisitor
通过初始化一个ClassWriter
和一个Printer
对象来打印我们需要的字节流信息。方便比较类文件及分析字节码文件的结构。