Java动态追踪技术探究(动态修改)

 Java动态追踪技术探究html

Java探针-Java Agent技术-阿里面试题java

秒懂Java动态编程(Javassist研究)git

能够用于在类加载的时候,修改字节码。github

Java agent(Java探针)技术

利用javaAgent和ASM字节码技术开发java探针工具,实现原理以下:面试

jdk1.5之后引入了javaAgent技术,javaAgent是运行方法以前的拦截器。咱们利用javaAgent和ASM字节码技术,在JVM加载class二进制文件的时候,利用ASM动态的修改加载的class文件,在监控的方法先后添加计时器功能,用于计算监控方法耗时,同时将方法耗时及内部调用状况放入处理器,处理器利用栈先进后出的特色对方法调用前后顺序作处理,当一个请求处理结束后,将耗时方法轨迹和入参map输出到文件中,而后根据map中相应参数或耗时方法轨迹中的关键代码区分出咱们要抓取的耗时业务。最后将相应耗时轨迹文件取下来,转化为xml格式并进行解析,经过浏览器将代码分层结构展现出来,方便耗时分析。编程

原理

Java对象的方法、函数存放在方法区。方法区中的数据是类加载时从class文件中提取出来的。class文件是咱们写的文件编译而来。浏览器

咱们能够在加载class字节码文件时,动态修改这个文件,从新加载class文件。可是不改变对象的属性,也不影响已经存在对象的状态。安全

java.lang.instrument.Instrumentation

Instrumentation接口,里面有2个方法:redefineClasses和retransformClasses。架构

一个是从新定义class,一个是修改class。这两个大同小异,都是替换已经存在的class文件,redefineClasses是本身提供字节码文件替换掉已存在的class文件,retransformClasses是在已存在的字节码文件上修改后再替换之。框架

固然在运行时直接替换很不安全。好比新引用不安全的类,删除某个属性,这些状况会发生异常,因此Instrumentation有不少限制,可是在方法先后加日志却是够了。

如今,咱们要修改字节码class文件。固然,最简单的是把修改后的Java文件从新编译一遍获得class文件,而后调用redefineClasses替换。可是,不少时候,咱们拿不到这个源码文件。

ASM

咱们都知道,Spring的AOP是基于动态代理实现的,Spring会在运行时动态建立代理类,代理类中引用被代理类,在被代理的方法执行先后进行一些神秘的操做。那么,Spring是怎么在运行时建立代理类的呢?动态代理的美妙之处,就在于咱们没必要手动为每一个须要被代理的类写代理类代码,Spring在运行时会根据须要动态地创造出一个类,这里创造的过程并不是经过字符串写Java文件,而后编译成class文件,而后加载。Spring会直接“创造”一个class文件,而后加载,创造class文件的工具,就是ASM了。

Attach API

Attach API 不是 Java 的标准 API,而是 Sun 公司提供的一套扩展 API,用来向目标 JVM ”附着”(Attach)代理工具程序的。有了它,开发者能够方便的监控一个 JVM,运行一个外加的代理程序。

Attach API 很简单,只有 2 个主要的类,都在 com.sun.tools.attach 包里面: VirtualMachine 表明一个 Java 虚拟机,也就是程序须要监控的目标虚拟机,提供了 JVM 枚举,Attach 动做和 Detach 动做(Attach 动做的相反行为,从 JVM 上面解除一个代理)等等 ; VirtualMachineDescriptor 则是一个描述虚拟机的容器类,配合 VirtualMachine 类完成各类功能。

Attach API说明

相对于jdk5只能经过启动脚本添加javaagent的方式植入代理,jdk6的动态attach也只是免去了修改启动脚本和不用重启的工做,并无添加其它新的特性,即便不使用动态attach而是使用脚本添加javaagent的方式也能够达到随时修改class定义的目的,不管经过什么方式咱们只要获取了 Instrumentation 实例,而后调用其addTransformer方法添加类转换器再调用retransformClasses就能够转换一个类的字节序列了,这里须要注意的是retransformClasses是jdk1.6定义的,jdk1.5只能使用redefineClasses,retransformClasses功能强大使用简单,但有不能修改方法签名,只能修改body等约束,redefineClasses则是一个可定细节制化的选择。

BTrace

BTrace是基于Java语言的一个安全的、可提供动态追踪服务的工具。BTrace基于ASM、Java Attach Api、Instruments开发,为用户提供了不少注解。依靠这些注解,咱们能够编写BTrace脚本(简单的Java代码)达到咱们想要的效果,而没必要深陷于ASM对字节码的操做中不可自拔。

BTrace主要有下面几个模块:

  1. BTrace脚本:利用BTrace定义的注解,咱们能够很方便地根据须要进行脚本的开发。
  2. Compiler:将BTrace脚本编译成BTrace class文件。
  3. Client:将class文件发送到Agent。
  4. Agent:基于Java的Attach Api,Agent能够动态附着到一个运行的JVM上,而后开启一个BTrace Server,接收client发过来的BTrace脚本;解析脚本,而后根据脚本中的规则找到要修改的类;修改字节码后,调用Java Instrument的reTransform接口,完成对对象行为的修改并使之生效。

整个BTrace的架构大体以下:

BTrace最终借Instruments实现class的替换。如上文所说,出于安全考虑,Instruments在使用上存在诸多的限制,BTrace也不例外。

Arthas

BTrace脚本在使用上有必定的学习成本,若是能把一些经常使用的功能封装起来,对外直接提供简单的命令便可操做的话,那就再好不过了。阿里的工程师们早已想到这一点,就在去年(2018年9月份),阿里巴巴开源了本身的Java诊断工具——Arthas。Arthas提供简单的命令行操做,功能强大。究其背后的技术原理,和本文中提到的大体无二。Arthas的文档很全面,想详细了解的话能够戳这里

 

另外:Javassist

动态编程

动态编程是相对于静态编程而言的,平时咱们讨论比较多的就是静态编程语言,例如Java,与动态编程语言,例如JavaScript。那两者有什么明显的区别呢?简单的说就是在静态编程中,类型检查是在编译时完成的,而动态编程中类型检查是在运行时完成的。所谓动态编程就是绕过编译过程在运行时进行操做的技术,在Java中有以下几种方式:

反射:原理也就是经过在运行时得到类型信息而后作相应的操做。

动态编译:动态编译是从Java 6开始支持的,主要是经过一个JavaCompiler接口来完成的。经过这种方式咱们能够直接编译一个已经存在的java文件,也能够在内存中动态生成Java代码,动态编译执行。

调用JavaScript引擎Java 6加入了对Script(JSR223)的支持。这是一个脚本框架,提供了让脚本语言来访问Java内部的方法。你能够在运行的时候找到脚本引擎,而后调用这个引擎去执行脚本。这个脚本API容许你为脚本语言提供Java支持。

动态生成字节码:这种技术经过操做Java字节码的方式在JVM中生成新类或者对已经加载的类动态添加元素。

动态生成字节码

操做java字节码的工具备两个比较流行,一个是ASM,一个是Javassit 。原理也都是利用了Java的设计原理:存在一个虚拟机执行字节码。这就使咱们在此处有了改变字节码的操做空间。

ASM :直接操做字节码指令,执行效率高,要是使用者掌握Java类字节码文件格式及指令,对使用者的要求比较高。

Javassist: 提供了更高级的API,执行效率相对较差,但无需掌握字节码指令的知识,对使用者要求较低。

应用层面来说通常使用建议优先选择Javassit,若是后续发现Javassit 成为了整个应用的效率瓶颈的话能够再考虑ASM.固然若是开发的是一个基础类库,或者基础平台,仍是直接使用ASM。

Javassit使用示例

原文

相关文章
相关标签/搜索