前言
arthas 可以通过 redefine 命令可以在不重启的情况下对正在运行的class文件进行变更。这篇主要是探究该原理并实现一下简易版的i_redefine的功能。整体实现逻辑:编写 agentmain ,为 Instrumentation 添加自定义Transformer,对加载后的class进行转换。将编写的 agentmain 打包成 Jar ,利用Attach API 发送指令到目标虚拟机,通知它进行loadAgent 。 使用到的技术栈:(Java agent(Java SE 6 )+ Java attach API+ asm)。
期间有一个问题困扰比较长:有什么方式知道一个class文件中对应的类的包名和类名?
看了arthas的源码才知道可以通过中 asm 的ClassReader方法实现。
agentMain
主要是为inst添加Transformer,并通知重新加载目标类:
1 | public class IAgentMain { |
再来看看转换类的具体逻辑,其中RedefineTransformer实现了ClassFileTransformer中的transform方法。
RedefineTransformer
如果是目标类,就通过 getBytesFromFile 加载需要替换的class文件:
1 | public class RedefineTransformer implements ClassFileTransformer { |
测试类
测试类比较简单,有两个方法的输出:
1 | public class Main { |
修改后的测试类
修改的比较简单,就是在sayBye下面添加了一行输出,通过 javac 对它进行编译,存放到路径F:/Main.class
.
1 | public class Main { |
通知目标主机
利用Java attach通知目标主机加载agentmain对应的Jar包,其中path是需要替换上的新class文件,pid是你运行测试类得到的pid号:
1 | public class JVMTIThread { |
验证
先启动Main的方法,将对应的 pid 与 修改后的class路径 填入到JVMTIThread 中并运行,结果:
1 | Hello World! |
可以看到运行的过程中Main的确被替换上了新的方法。