今天从jvm大神"你假笨"的公众号上,看到一个jdk 9+版本的编译bug,记录一下:
public class JavacEvalBug{ private static String[] array = {""}; static int test(){
System.out.println("evaluated!");
return 0;
} public static void main(String[] args) {
//相当于int index=test(); array[index] +="a";
array[test()] += "a";
} }
test()方法里输出了一个固定字符串,上面这段代码,如果是在jdk8版本里,执行后,只会输出:evaluated! 一次(这符合预期,因为test()只调用了1次)
但如果把jdk升级到jdk9或10,再次编译运行,evaluated!就会输出次,即test()方法会多执行了1次,如果test()方法是复杂的业务逻辑,比如创建订单/库存扣减之类,这就成大问题了。
原因在于jdk8与jdk9+的编译机制不同,javap -verbose JavacEvalBug 使用这个命令,可以看到编译细节:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=1, args_size=1
0: new #5 // class java/lang/StringBuilder
3: dup
4: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
7: getstatic #7 // Field array:[Ljava/lang/String;
10: invokestatic #8 // Method test:()I
13: dup2_x1
14: aaload
15: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: iconst_1
19: invokevirtual #10 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
22: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: aastore
26: return
jdk8上,从第10行看,只调用了1次,如果切换到jdk9+,则会变成:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
0: getstatic #5 // Field array:[Ljava/lang/String;
3: invokestatic #6 // Method test:()I
6: getstatic #5 // Field array:[Ljava/lang/String;
9: invokestatic #6 // Method test:()I
12: aaload
13: invokedynamic #7, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
18: aastore
19: return
LineNumberTable:
明显可以看到Method test:()I 调用了2次。具体详情分析,大神说是以后会详细分析,大概是字符串拼写的方式,jdk9以后做了变化。如果把string数组换成其它类型比如int
public class JavacEvalBug{
private static int[] array = {0}; static int test(){
System.out.println("evaluated!");
return 0;
} public static void main(String[] args) {
//相当于int index=test(); array[index] +=1;
array[test()] += 1;
}
}
就正常了
2018-07-28 更新,在jdk 10.0.2版本上,该bug已修复