2013-06-22 21 views
7

Это странно, но код говорит больше, чем слова, поэтому посмотрите на тест, чтобы увидеть, что я делаю. В моей текущей настройке (обновление Java 7 21 на 64-разрядной Windows) этот тест завершился неудачно с ArrayIndexOutOfBoundsException, но, заменив код метода тестирования на код с комментариями, он работает. И мне интересно, есть ли какая-то часть спецификации Java, которая объясняла бы почему.JVM ошибка? Значение поля кэшированного объекта вызывает ArrayIndexOutOfBoundsException

Мне кажется, что «майкл нестеренко» предположил, что значение поля массива кэшируется в стеке, перед вызовом метода и не обновляется при возврате из вызова. Я не могу сказать, является ли это ошибкой 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")]); 
    } 
} 
+2

Догадка, когда вы вызываете 'grow' из массива, он уже находится в стеке, и поэтому ссылка не обновляется, но если вы вызываете' grow' before и затем используете индекс, ссылка на массив загружается после его обновления и таким образом, он работает. просто догадка. возможно, просмотр байтового кода может помочь –

+1

Я бы не назвал его «кэшированным в стеке». Я думаю, что это вопрос Java (langauge). Вы ссылаетесь на имя, которое разрешено * до *, используя/применяя разрешенное значение, связанное с ним. Поэтому, если имя перераспределено между ними, вы получили неправильное значение. –

+1

Вот более простой тестовый пример: 'public class TestAIOOB {static Object [] array; static int reassign() {array = new Object [] {new Object()}; return 0; } public static void main (String [] args) {System.out.println (array [reassign()]); }} '. Это бросает (как и должно), и вы спрашиваете, почему. –

ответ

6

Это хорошо определяется Java Language Specification: оценить x[y], первый x оценивается, а затем y оценивается. В вашем случае x оценивает String[] с нулевыми элементами. Затем y изменяет переменную-член и вычисляет 0. Пытается получить доступ к 0-му элементу уже возвращенного массива. Тот факт, что изменения члена array не влияет на поиск массива, потому что мы смотрим на String[], что array ссылается на момент его оценки.

+0

Прямо там, как первая точка в ссылке. Отлично. –

0

Сравните скомпилированный код Java с помощью javap -c TestAIOOB

незакомментированной код:

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 а во втором это происходит тер.

+2

Это объясняет поведение, но не объясняет, почему он был скомпилирован таким образом. Должно быть что-то в JLS ... –

2

Такое поведение поручено JLS. Per 15.13.1: «Выражение доступа к массиву оценивается с использованием следующей процедуры: во-первых, оценивается ссылочное выражение массива. Если эта оценка завершается внезапно, тогда доступ к массиву завершается внезапно по той же причине, и выражение индекса не оценивается. В противном случае, выражение индекса оценивается. [...] ".

Смежные вопросы