工作填坑记,agent导致客户启动服务报错:java.lang.VerifyError: Bad local variable type

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/dataiyangu/article/details/102708924

背景

工作中的问题,客户因为嵌入javaagent导致启动失败,久久解决不出来,记录下心酸历程
在这里插入图片描述
客户的类和方法加了马赛克,如果正在阅读的你还是发现泄露了某些信息,请告知我,多谢~

分析

有一种可能:

https://yujisoftware.hatenablog.com/entry/20161215/1481819208

局部变量为“最终字符串”,并且该变量在条件运算符中使用
例如,以下代码适用:
公共 类 JDK8149330 {
public static void main(String [] args){
final String y = “ Y” ;
final String n = “ N” ;
System.out.println(true?y:n);
}
}
顺便说一句,此错误发生在Java SE 8(第一版)和Update 92之间,而对于Java SE 7则不会发生。
但是
客户是1.6的,所以不存在这种可能

自己摸索吧

哈哈,一年前我是学习过jvm相关东西的还好,正好复习下。

每一个方法从调用开始到执行完成的过程,就对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量
注意上图中的V @83: aload_2
上面的aload_2,意思是加载第二个局部变量。(字节码命令有兴趣的看一看,很舒服的。)

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

栈帧、局部变量表、操作数栈

通过跟客户要代码,发现,上面报错的方法只有一个参数

上面的错误日志中
Reason:
Type top (current frame, locals[2]) is not assignable to reference type
原因也写了

在stackoverflow 中有相关的解释,跟我这个几乎一模一样
https://stackoverflow.com/questions/27487417/java-lang-verifyerror-bad-local-variable-type-after-bytecode-instrumentation
注意最后的话:

您一定要使用该标志COMPUTE_FRAMES来ClassWriter为您做到这一点…… – Holger 2014年 12月15日

观察我们javaagent中的代码,javaagent中classfileBuffer 是类的字节码的意思,是transform方法中的最后一个参数

public byte[] transform(ClassLoader classLoader, String className,
			Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
			byte[] classfileBuffer) throws IllegalClassFormatException {}

这里再额外说明一下ClassWriter构造器的另外一种参数。如果我们使用了ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);那么COMPUTE_MAXS 就是替我们计算局部变量表和操作数栈的大小,这时候我们还是需要调用 mv.visitMaxs(2, 2);方法,但是可以传递任意参数,因为COMPUTE_MAXS的方式会帮助我们重新计算局部变量和操作数的size。还需要注意的是COMPUTE_MAXS不会为我们计算StackMapFrame,而COMPUTE_FRAMES既会计算栈size,也会计算StackMapFrame,当然使用COMPUTE_MAXS会让ClassWriter慢10%,而使用COMPUTE_FRAMES会慢20%(数据来自ASM官方说明文档,这里就不再测试)。当然,我觉得,如果使用起来的话,我还是宁可用自动挡。这样可以适当增强代码可读性,减少了代码维护成本。如果有很好的算法(如果你确定你比ASM框架写出了更好的实现算法)来计算这些参数,当然手动挡也是一种乐趣。
https://www.iteye.com/blog/yunshen0909-2222638(很厉害)

栈图:
https://blog.csdn.net/lijingyao8206/article/details/46715405
StackMapFrame就是栈图里面的一个entry

在Java 6版本之后JVM在class文件中引入了栈图(StackMapTable)属性
客户的项目是1.6的。

而我们有部分代码是这样的:

int flags = classfileBuffer[7] > (byte)0x32 ? ClassWriter.COMPUTE_FRAMES : 0;

这里解释下,classfileBuffer是传进来的class类的字节码信息,至于类的字节码信息,有兴趣的人自行查阅,这里的第七位是jdk版本的意思,我也查不到,是自己测试出来的,(byte)0x32是16进制的50的意思,所以这句话的意思是如果jdk版本大于1.6的话创建classwriter的时候用ClassWriter.COMPUTE_FRAMES,否则用0,而客户用的是1.6,所以是用的0,而上面分析说1.6之后有了栈图,需要用ClassWriter.COMPUTE_FRAMES创建classwriter,所以问题有点眉目了

解决

最终这个问题,目前我想到的就是写死用ClassWriter.COMPUTE_FRAMES。

结尾

这个问题面试的时候应该能够让面试官一愣一愣的吧,里面的东西不少呢,以我的水平看。

猜你喜欢

转载自blog.csdn.net/dataiyangu/article/details/102708924
今日推荐