工作中的问题,客户因为嵌入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。
这个问题面试的时候应该能够让面试官一愣一愣的吧,里面的东西不少呢,以我的水平看。