Странная запись таблицы исключений, созданная Sun javac
Учитывая эту программу:
class Test {
public static void main(String[] args) {
try {
throw new NullPointerException();
} catch (NullPointerException npe) {
System.out.println("In catch");
} finally {
System.out.println("In finally");
}
}
}
Sun javac
(v 1.6.0_24) создает следующий байт-код:
public static void main(java.lang.String[]);
// Instantiate / throw NPE
0: new #2; // class NullPointerException
3: dup
4: invokespecial #3; // Method NullPointerException."<init>":()V
7: athrow
// Start of catch clause
8: astore_1
9: getstatic #4; // Field System.out
12: ldc #5; // "In catch"
14: invokevirtual #6; // Method PrintStream.println
17: getstatic #4; // Field System.out
// Inlined finally block
20: ldc #7; // String In finally
22: invokevirtual #6; // Method PrintStream.println
25: goto 39
// Finally block
// store "incomming" exception(?)
28: astore_2
29: getstatic #4; // Field System.out
32: ldc #7; // "In finally"
34: invokevirtual #6; // Method PrintStream.println
// rethrow "incomming" exception
37: aload_2
38: athrow
39: return
Со следующей таблицей исключений:
Exception table:
from to target type
0 8 8 Class NullPointerException
0 17 28 any
28 29 28 any
Мой вопрос: Почему он включает эту последнюю запись в таблицу исключений?!
Насколько я понимаю, в основном говорится: "Если astore_2
выдает исключение, ловить его и повторять ту же инструкцию".
Такая запись создается даже с пустыми предложениями try/catch/finally, такими как
try {} catch (NullPointerException npe) {} finally {}
Некоторые наблюдения
- Компилятор Eclipse не создает такую запись таблицы исключений
- Спецификация JVM не документирует исключения во время выполнения для инструкции
astore
.
- Я знаю, что JVM имеет право бросать
VirtualMachineError
для любой команды. Я предполагаю, что особенная запись предотвращает распространение таких ошибок из этой инструкции.
Ответы
Ответ 1
Есть только два возможных объяснения: компилятор содержит ошибку или помещает своего рода водяной знак для неясных причин.
Эта запись, безусловно, подделана, потому что любое исключение, созданное блоком finally, должно отправлять поток выполнения внешнему обработчику исключений или, наконец, блокировать, но никогда не "запускать" тот же последний блок.
Кроме того, хорошим доказательством того, что это ошибка/водяной знак, является тот факт, что Eclipse (и, возможно, другие компиляторы Java) не генерируют такую запись, и даже поэтому классы, созданные Eclipse, отлично работают на Sun JVM.
Тем не менее, этот пост интересен тем, что кажется, что файл класса действителен и проверен. Если бы я был разработчиком JVM, я бы проигнорировал эту запись и заполнил ошибку для Sun/Oracle!
Ответ 2
Глядя на исходный код OpenJDK 7, я бы рискнул угадать причину
что последняя запись таблицы исключений 28 29 28 any
- это потому, что код, который
обрабатывает байт-код astore
(см. код, начиная с строки 1871), может
бросить java.lang.LinkageError
исключение, если всплывающее значение
из стека операндов не является типом returnAddress
или reference
(см.
Спецификация виртуальной машины Java для astore), и они хотят этого
условие ошибки для отображения на трассировке стека.
В случае, если в стеке операндов существует неправильный тип операнда, JVM будет
очистите стек операнда (избавившись от этого плохого операнда), поставьте LinkageError
в стеке операндов и снова выполнить байт-код astore
, на этот раз
успешно выполнив байт-код astore
, используя JVM при условии LinkageError
ссылка на объект. Дополнительную информацию см. В документации athrow.
Я бы очень сильно подозревал основную причину бросания LinkageError
во время
astore
обработка происходит из-за сложности подпрограмм JSR/RET вводят в проверку байт-кода (OpenJDK изменения
6878713, 6932496 и 7020373
недавние доказательства JSR
непрерывной сложности; Я уверен, что Sun/Oracle имеет другие
закрытые исходные тесты, которые мы не видим в OpenJDK). OpenJDK 7020373 изменение использует LinkageError
для проверки/недействительности результатов теста.
Ответ 3
Я понимаю, что вторая запись таблицы исключений - это неявное предложение catch all, добавленное компилятором для покрытия любых исключений/ошибок, брошенных в тело или обработчиков catch, а третья запись - это защита от неявного улова, чтобы заставить поток через окончательное выполнение.