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已修复

作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
上一篇:[关注&学习]patterns & practices Application Architecture Guide - v2.0 (Beta 1 Release)


下一篇:[cocos2dx-lua]"Hello Lua"分析