今天從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!就會輸出2次,即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已修復
