字节码实战--手写一个btrace
简易的btrace需求
偶现的方法执行慢,我们是可以用jstack捕捉到的,但是慢到什么地步却是不一定知道的,现在就需要在不重启应用的情况下,获取方法执行的时间。
需求特点
- 应用不重启
- 获取方法执行时间
技术选型
想要打印出时间,起码想到的是aop的方式。常规的方法是必须重启应用才能加的,典型的就是spring的aop,如果允许修改代码的话可以使用动态代理,或者自己写死到代码里。
操作方案
动态代理或者写死到代码
这种情况对代码的入侵太高了,如果要去掉这个功能,修改代码也是很麻烦的。
spring aop
这个需要依赖spring框架来做。可以不修改代码,但是不能做到动态生效。
javaagent
javaagent在1.6之后可以使用远程attach的方式,进行类的重新转化。这个是满足需求的。这个不了解的话可以去网上查查看。
字节码增强技术
方式选择好以后,那么关键点就是如何选择修改字节码的框架。
javassist
javassist是相对好用的,不过他想要在一个方法前后加代码,是通过方法包装来的,就是命名一个新的方法名和现在执行的方法名一样,然后把旧的方法重新命名。流程如下:
转化前:
public void hello(){
int a=0;
}
转化后
public void hello(){
xxx
helloxx();
xxx
}
public void helloxx(){
int a=0;
}
这种情况是transform的做法,但是retransform有不能新增方法的限制。jdk的文档描述如下
The retransformation must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance.
asm
asmapi操作比较麻烦。但是可以直接修改方法体的内容。
转化前:
public void hello(){
int a=0;
}
转化后:
public void hello(){
xxx
int a=0;
xxx
}
asm的做法也有两种方式,一种是增加方法调用,然后把传递的值保存在threadlocal里,另外一种就是把所有的内容写到方法里中。这两种都可以满足需求,第一种实现方式还简单一些,不用新增本地变量。
实现方式
修改字节码的代码比较枯燥,所有直接附上代码地址,此次使用的是asm增加方法局部变量的方式做的,这样的demo网上博客没有,asm的手册中也没有这样的例子,如果有兴趣的可以去看看
https://github.com/xpbob/lightTrace