JVM error? The value of the Cached Object field raises an ArrayIndexOutOfBoundsException

It's weird, but the code says more than words, so look at the test to see what I'm doing. In my current setup (updating Java 7 21 on 64-bit Windows) this test failed with an ArrayIndexOutOfBoundsException, but, replacing the test method code with the commented code, it works. And I wonder if there is any part of the Java specification that would explain why.

It seems to me, as Michael Nesterenko suggested, that the value of the array field is cached on the stack, before the method is called, and is not updated when it returns from the call. I can’t say if this is a JVM bug or a documented “optimization”. Multithreaded or "magic" is not involved.

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")]); } } 
+7
source share
3 answers

This is well defined. Java Language Specification : to evaluate x[y] , first x is evaluated, and then y is evaluated. In your case, x evaluates to String[] with null elements. Then y changes the member variable and evaluates to 0 . Attempts to access the 0th element of an already returned array. The fact that the array member is changing does not affect the search for the array, because we are looking at String[] , which array refers to when it was evaluated.

+6
source

This behavior is provided by JLS. Per 15.13.1 , "An array access expression is evaluated using the following procedure: first, the array reference expression is evaluated. If this evaluation terminates abruptly, access to the array terminates abruptly for the same reason, and the index expression is not evaluated. Otherwise if the index expression is evaluated. [...] ".

+2
source

Compare compiled Java code with javap -c TestAIOOB

Incomplete code:

 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 

Commented code:

 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 

In the first case, getfield happens before grow called, and in the second, after.

0
source

All Articles