jdk9+版本的bug


今天從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已修復


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM