java探针利用了javaAgent + ASM字节码注入工具实现了动态修改类文件的功能。像skywalking和arthas都使用到了这个技术。
具体原理为:
jdk1.5以后引入了javaAgent技术,javaAgent是运行方法之前的拦截器。我们利用javaAgent和ASM字节码技术,在JVM加载class二进制文件的时候,利用ASM动态的修改加载的class文件,在监控的方法前后添加计时器功能,用于计算监控方法耗时,同时将方法耗时及内部调用情况放入处理器,处理器利用栈先进后出的特点对方法调用先后顺序做处理,当一个请求处理结束后,将耗时方法轨迹和入参map输出到文件中,然后根据map中相应参数或耗时方法轨迹中的关键代码区分出我们要抓取的耗时业务。最后将相应耗时轨迹文件取下来,转化为xml格式并进行解析,通过浏览器将代码分层结构展示出来,方便耗时分析。
上篇我们介绍了JavaAgent的基本使用,下面介绍如何去动态的修改类的字节码文件,这个才是agent实现更强大功能的核心所在!
Instrumentation接口
Instrumentation接口位于jdk1.6包java.lang.instrument包下,Instrumentation指的是可以独立于应用程序之外的代理程序,可以用来监控和扩展JVM上运行的应用程序,相当于是JVM层面的AOP。
功能:
监控和扩展JVM上的运行程序,它可以替换和修改java类的字节码以便采集数据,用于监控,性能统计,覆盖率分析,事件记录等。可以用在程序启动时,也可以用于程序运行时动态attach。
比如说一个Java程序在JVM上运行,这时如果需要监控JVM的状态,除了使用JDK自带的jps等命令之外,就可以通过instrument来更直观的获取JVM的运行情况;
或者一个Java方法在JVM中执行,如果我想获取这个方法的执行时间又不想改代码,常用的做法是通过Spring的AOP来实现,而AOP通过面向切面编程,而instrument是在JVM层面上直接改动java方法来实现。
public interface Instrumentation{
//添加ClassFileTransformer
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
//添加ClassFileTransformer
void addTransformer(ClassFileTransformer transformer);
//移除ClassFileTransformer
boolean removeTransformer(ClassFileTransformer transformer);
//是否可以被重新定义
boolean isRetransformClassesSupported();
//重新定义Class文件
void redefineClasses(ClassDefinition... definitions)
throws ClassNotFoundException, UnmodifiableClassException;
//是否可以修改Class文件
boolean isModifiableClass(Class<?> theClass);
//获取所有加载的Class
@SuppressWarnings("rawtypes")
Class[] getAllLoadedClasses();
//获取指定类加载器已经初始化的类
@SuppressWarnings("rawtypes")
Class[] getInitiatedClasses(ClassLoader loader);
//获取某个对象的大小
long getObjectSize(Object objectToSize);
//添加指定jar包到启动类加载器检索路径
void appendToBootstrapClassLoaderSearch(JarFile jarfile);
//添加指定jar包到系统类加载检索路径
void appendToSystemClassLoaderSearch(JarFile jarfile);
//本地方法是否支持前缀
boolean isNativeMethodPrefixSupported();
//设置本地方法前缀,一般用于按前缀做匹配操作
void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix);
}
要是定义了操作java类的class文件方法,这里又涉及到了ClassFileTransformer接口,这个接口的作用是改变Class文件的字节码,返回新的字节码数组,源码如下:
public interface ClassFileTransformer{
byte[] transform(ClassLoader loader, String className, Class<?