Instrumentation(3)
摘要: Instrumentation 类加载过程 Instrumentation与Transformer Instrumentation与Transformer的关系 Instrumentation接口简介 触发字节码转换的途径 Transformer分类 Transformer调度过程...
Instrumentation
类加载过程
Instrumentation与Transformer
Instrumentation与Transformer的关系
Instrumentation接口简介
触发字节码转换的途径
Transformer分类
Transformer调度过程
Transformer如何实现
redefineClasses、retransformClasses
注意事项
Instrumentation
Instrumentation是JDK提供的用于支撑字节码修改的服务。在某些情况下,需要对Java程序进行性能优化、监控、分析、日志记录等工作时,就需要通过字节码修改来完成了。常见的字节码修改工具有:BCEL、ASM、CGLib、Javassist。Instrumentation并不是一个字节码修改工具,它是用来为字节码修改工具提供入口的,也就是字节码修改工具要通过Instrumentation才能接入到一个Java进程里,才能进行字节码修改操作。
类加载过程
如果去网上搜索类加载过程,能搜到很多,那些文章大致可以分为两类:
1: 类从加载到虚拟机到卸载,它的整个生命周期包括:加载(Loading),验证(Validation),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using)和卸载(Unloading)。其中,验证、准备和解析部分被称为连接(Linking)。然后再说一下每个过程做的什么。但是这些文章对于理解Instrumentation ,帮助并不大。 2:第二类是讲述ClassLoader机制的、但是讲述的范围无非就是delegate加载,或者是子类优先加载两种方式罢了。
总之、很少有关注类加载时要注意什么、类加载后又做了什么。而这些不被关注的地方恰恰是理解Instrumentation所需要的。 ClassLoader.loadClass()的过程是: ClassLoader.loadClass() ——>ClassLoader.findClass() ——>defineClass(byte[]) ——>resolveClass(Class) 当一个ClassLoader加载一个类时,在找到find到.class文件后,会调用defineClass()将字节码转换成Class对象。然后再通过resolveClass(Class)进行linking(连接)操作,连接完毕就可以使用了。 在defineClass()中,一般是分为3个步骤进行:
·设置protectionDomain
·通过本地代码(defineClass1)来进行类定义。
·后置处理。
Instrumentation与Transformer
Instrumentation与Transformer的关系
Transformer是字节码转换的接口,Instrumentation是管理Transformer、调度Transformer进行字节码转换的门面。两者关系如下: TransformerManager用于管理、调度Transformer的,但是这个类是不可见的(不能直接在代码里使用的)。Instrumentation是一个门面,在程序中,直接使用的是Instrumentation的实例。
Instrumentation接口简介
当执行Instrumentation的addTransformer、removeTransformer方法时,最终是调用了TransformerManager的addTransformer、removeTransformer。以此来管理Transformer。 Instrumentation的retransformClasses、redefineClasses是用于通知TransformerManager调度字节码转换的。通常在执行这两个方法之前,要先判断是否支持相应的操作,即要先调用isRedefineClassesSupported、isRetransformClassesSupporte。这是因为不同的JVM对JAVA标准支持不同,可能有些JDK不支持这些操作。即便是Oralce(Sun)的JDK,低版本的也是不支持这些操作的。
另外,使用Instrumentation,还可以动态的向BootstrapClassLoader、SystemClassLoader的搜索路径下添加Jar、classes等。
触发字节码转换的途径 上一小节中说了,通过Instrumentation的retransformClasses、redefineClasses通知TransformerManager调度Transformer来进行字节码转换。其实还有一种方式,也可以通知字节码转换。
在类加载过程这一小节的最后提到了ClassLoader.defineClass1()这个方法用于进行类的定义。这个方法是在本地代码中实现的,但这个过程也会通知TransformerManager调度Transformer来进行字节码转换。
所以,共有三种方式来通知Transformer来进行字节码转换。而这三种方式,也有另外一种叫法:加载类时、重定义类时、重转换类时。
Transformer分类
Transformer可以分为两类:可重转换的Transformer、不可重转换的Transformer。
任何一个Transformer都可以用于加载类时、重定义类时进行转换。如果是可重转换的Transformer,也可以在重转换时进行转换。
注册Transformer时(通过Instrumentation.addTransformer(ClassFileTransformer, canRetransform)来注册),将第二个参数设为false就是不可重转换,设置为true就是可重转换的转换器。
Transformer的调度过程 当存在多个转换器时,转换将由transform调用链组成。也就是说,一个transform调用返回的byte数组将成为下一个调用的输入(通过classfileBuffer参数)。 转换将按以下顺序进行:
·不可重转换转换器
·不可重转换本地(native)转换器
·可重转换转换器
·可重转换本地(native)转换器
对于重转换,不会调用不可重转换转换器,而是重用前一个转换的结果。对于所有其他情况,调用此方法。在每个这种调用组中,转换器将按照注册的顺序调用。本机转换器由 Java 虚拟机 Tool 接口中的 ClassFileLoadHook 事件提供。
Transformer如何实现
ClassFileTransformer接口中只有一个方法: 它是用于将已有的字节码classfileBuffer转换成新的字节码。所以在这里面,可以使用字节码工具来修改。
参数说明:
loader :加载该类的类加载器。如果是boostrapClassLoader,则为null。
className:类完全限定名,用/分隔。例如(java/util/List)
classBeingRedeined 如果是null,说明这次转换是在类加载时发起的。
protectionDomain:要定义或者重定义的类的保护域。
classfileBuffer:要被修改的字节码。(这些字节码可能是第一次加载、也可能是第1..n次被转换)。
返回值: 如果不做任何转换,则要返回null。 如果转换器抛出异常(未捕获的异常),后续转换器仍然将被调用并加载,仍然将尝试重定义或重转换。因此,抛出异常与返回 null 的效果相同。
redefineClasses与retransformClasses
redefineClasses:
·使用提供的类文件重定义提供的类集。
·此方法用于替代类的定义,而不引用现有的类文件字节,这与 fix-and-continue 调试过程中重新编译源代码时所做的一样。需要转换现有类文件字节的地方(例如,字节码检测中)应该使用retransformClasses。
·此方法在一个集合上操作,以便允许同时对多个类进行相互依赖的更改(重定义类 A 要求重定义类 B)。
·如果重定义的方法有活动的堆栈帧,那么这些活动的帧将继续运行原方法的字节码。将在新的调用上使用此重定义的方法。
·此方法不会引起任何初始化操作,JVM 惯例语义下发生的初始化除外。换句话说,重定义一个类不会引起其初始化方法的运行。静态变量的值将与调用之前的值一样。 ·重定义类的实例不受影响。
·重定义可能会更改方法体、常量池和属性。重定义不得添加、移除、重命名字段或方法;不得更改方法签名、继承关系。在以后的版本中,可能会取消这些限制。在应用转换之前,类文件字节不会被检查、验证和安装。如果结果字节错误,此方法将抛出异常。
·如果此方法抛出异常,则不会重定义任何类。
retransformClasses:
·此方法在一个集合上操作,以便允许同时对多个类进行相互依赖的更改(重转换类 A 要求重转换类 B)。
·如果重转换的方法有活动的堆栈帧,那么这些活动的帧将继续运行原方法的字节码。重转换的方法将用于新的调用。
·此方法不会引起任何初始化操作,JVM 惯例语义下发生的初始化除外。换句话说,重定义一个类不会引起其初始化方法的运行。静态变量的值将与调用之前的值一样。
·重转换类的实例不受影响。
·重转换可能会更改方法体、常量池和属性。重转换不得添加、移除、重命名字段或方法;不得更改方法签名、继承关系。在以后的版本中,可能会取消这些限制。在应用转换之前,类文件字节不会被检查、验证和安装。如果结果字节错误,此方法将抛出异常。
·如果此方法抛出异常,则不会重转换任何类。 不论是redefineClasses,还是retransformClasses,都有这样的:
1、如果重转换(或者重定义)的方法有活动的堆栈帧,那么这些活动的帧将继续运行原方法的字节码。重转换(或者重定义)的方法将用于新的调用。这一条说明了,在重转换(或者重定义)时,一个类的定义可能会存在多个版本。 在JDK 8下,有方法可以是default 的,故而可能会引起ICCE(IncompatibleClassChangeError),这点在IBM JDK8下发现,在Oralce JDK8下没有发现。
2、重转换(或者重定义)可能会更改方法体、常量池和属性。重转换不得添加、移除、重命名字段或方法;不得更改方法签名、继承关系。 但如果这次转换是由类加载引起的,是可以不遵循这条规则的。
两者差异: redefineClasses、retransformClasses 基本是相同的,主要 在不同点是retransformClasses可以跨多个agent的。也就是由retransformClasses 发起的转换,会调用每个agent中添加的transformer。 注意事项 为Agent开启redefine功能: 在javaagent的MANIFEST.MF里设置`Can-Redefine-Classes:true` 为Agent开启retransform功能: 在javaagent的MANIFEST.MF文件里定义了`Can-Retransform-Classes:true`
Instrumentation(3)的更多相关文章
- Instrumentation(1)
Instrumentation介绍: JavaInstrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序.这种监测和协助包括但不限于获取J ...
- Android单元测试初探——Instrumentation(转载)
学习Android有一段时间了,虽然前段时间对软件测试有了一些了解,不过接触android的单元测试却是头一次.这几天在物流大赛上也用了不少时间,所以对于android的单元测试没有太深入的研究,所以 ...
- android测试之——Instrumentation(一)
以下是本人原创,如若转载和使用请注明转载地址.本博客信息切勿用于商业,可以个人使用,若喜欢我的博客,请关注我,谢谢!博客地址 感谢您支持我的博客,我的动力是您的支持和关注!如若转载和使用请注明转载地址 ...
- Spring学习之Jar包功能介绍(转)
spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar, aspects.jar, spring-portlet.jar, and spring-hibernate2. ...
- spring jar包解读(转)
作者:http://www.cnblogs.com/leehongee/archive/2012/10/01/2709541.html spring.jar 是包含有完整发布模块的单个jar 包.但是 ...
- Instrumentation 功能介绍(javaagent)
利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能从本地代码中 ...
- 曹工说Spring Boot源码(14)-- AspectJ的Load-Time-Weaving的两种实现方式细细讲解,以及怎么和Spring Instrumentation集成
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- 曹工说Spring Boot源码(25)-- Spring注解扫描的瑞士军刀,ASM + Java Instrumentation,顺便提提Jar包破解
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- Spring4读书笔记(1)-模块
Srping主要模块 Core Container spring-core,spring-beans: 提供基础功能,包括IoC和DI等特性.对依赖起到解耦作用(BeanFactory). sprin ...
随机推荐
- 关于linux防火墙
1.防火墙是什么:防火墙就是用于实现Linux下访问控制的功能的,它分为硬件的或者软件的防火墙两种.无论是在哪个网络中,防火墙工作的地方一定是在网络的边缘.而我们的任务就是需要去定义到底防火墙如何工作 ...
- js中用var与不用var的区别
var num = 1: 是在当前域中声明变量. 如果在方法中声明,则为局部变量(local variable):如果是在全局域中声明,则为全局变量. 而 num = 1: 事实上是对属性赋值操作.
- 大数据项目中的Oracle查询优化
今天发现自己之前写的一些SQL查询在执行效率方面非常不理想,于是尝试做了些改进. 需求为查询国地税表和税源表中,国税有而税源没有的条目数,之前的查询如下: SELECT COUNT(NAME) FRO ...
- sql语言不经常用,复习
sql语言不经常用,每次再用都隔好久的时间,以致最基本的都想不起来了,只好转一篇记着= - 找的时候方便 SQL分类: DDL-数据定义语言(CREATE,ALTER,DROP,DECLARE) ...
- Oracle100w数据大表割接
[现网问题] 最近在给咪咕做视频后台管理,移动那边希望页面上,码流字段可以支持1位小数,如8.0.自己查看数据库,发现码流字段是Number整型,也就是要换类型,打算直接换成varchar2.因为自己 ...
- AngularJS数据绑定中数据监控的机制说明
from : http://docs.angularjs.org/guide/scope When the browser calls into JavaScript the code execute ...
- 干货!从Tomcat执行流程了解jsp是如何被解析的,错误提示是哪里生成的。
一.关于Tomcat组成 先上张图: Tomcat组成: 1. Server:代表整个 servlet 容器,如Tomcat,JBoss之类的. 2. Service:它由一个或者多 ...
- 分布式任务调度平台XXL-JOB
<分布式任务调度平台XXL-JOB> 一.简介 1.1 概述 XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源代码并 ...
- redis安装、使用
官网:http://redis.io/ github地址:https://github.com/antirez/redis 简介: redis是一个key-value存储系统.和Mem ...
- java基础- Collection和map
使用构造方法时,需要保留一个无参的构造方法 静态方法可以直接通过类名来访问,而不用创建对象. -- Java代码的执行顺序: 静态变量初始化→静态代码块→初始化静态方法→初始化实例变量→代码块→构造方 ...