JVM错误? Cached Object字段值导致ArrayIndexOutOfBoundsException

时间:2013-06-22 21:50:56

标签: java jvm indexoutofboundsexception

这有点奇怪,但代码说的多于单词,所以看看测试看看我在做什么。在我当前的设置(Windows 64位上的Java 7更新21)中,此测试因ArrayIndexOutOfBoundsException而失败,但是将测试方法代码替换为注释代码,它可以正常工作。我想知道Java规范中是否有任何部分可以解释原因。

在我看来,正如“michael nesterenko”建议的那样,在调用方法之前,数组字段的值被缓存在堆栈中,并且在从调用返回时不会更新。我不知道它是JVM错误还是记录在案的“优化”。没有涉及多线程或“魔术”。

public class TestAIOOB {
    private String[] array = new String[0];
    private int grow(final String txt) {
        final int index = array.length;
        array = Arrays.copyOf(array, index + 1);
        array[index] = txt;
        return index;
    }
    @Test
    public void testGrow() {
        //final int index = grow("test");
        //System.out.println(array[index]);
        System.out.println(array[grow("test")]);
    }
}

3 个答案:

答案 0 :(得分:6)

Java Language Specification明确定义:评估x[y],首先评估x,然后评估y。在您的情况下,x评估为String[],零元素。然后,y修改成员变量,并计算为0。尝试访问已返回的数组的第0个元素失败。成员array更改的事实与数组查找无关,因为我们正在查看在评估它时String[]引用的array

答案 1 :(得分:2)

此行为是由JLS强制执行的。 Per 15.13.1,“使用以下过程计算数组访问表达式:首先,计算数组引用表达式。如果此评估突然完成,则数组访问突然完成,原因相同且索引表达式不是否则,将评估索引表达式。[...]“。

答案 2 :(得分:0)

使用javap -c TestAIOOB

比较已编译的Java代码

未注释的代码:

public void testGrow();
  Code:
   0:   getstatic       #6; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   aload_0
   4:   getfield        #3; //Field array:[Ljava/lang/String;
   7:   aload_0
   8:   ldc     #7; //String test
   10:  invokespecial   #8; //Method grow:(Ljava/lang/String;)I
   13:  aaload
   14:  invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/St
ing;)V
   17:  return

评论代码:

public void testGrow();
  Code:
   0:   aload_0
   1:   ldc     #6; //String test
   3:   invokespecial   #7; //Method grow:(Ljava/lang/String;)I
   6:   istore_1
   7:   getstatic       #8; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_0
   11:  getfield        #3; //Field array:[Ljava/lang/String;
   14:  iload_1
   15:  aaload
   16:  invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   19:  return

首先getfield在调用grow之前发生,第二次发生在{{1}}之后。