JavaSe:Instrumentation

摘要: Instrumentation 类加载过程 Instrumentation与Transformer Instrumentation与Transformer的关系 Instrumentation接口简介 触发字节码转换的途径 Transformer分类 Transformer调度过程 Transformer如何实现 redefineClasses、retransformClasses 注意事项 Instrumentation Instrumentation是JDK提供的用于支撑字节码修改的服务。
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

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • 0 介绍 使用 Instrumentation,使得开发者可以构建一个独立于应用程序的代理程序(Agent),用来...
    七寸知架构阅读 28,118评论 3 85
  • 面试必背 会舍弃、总结概括——根据我这些年面试和看面试题搜集过来的知识点汇总而来 建议根据我的写的面试应对思路中的...
    luoyangzk阅读 6,751评论 6 173
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,605评论 18 399
  • 那天使对他们说:“不要惧怕!我报给你们大喜的信息,是关乎万民的;因今天在大卫的城里,为你们生了救主,就是主基督。你...
    占从Zoey阅读 1,275评论 0 0