Подтвердить что ты не робот

Как JVM гарантирует выполнение блока finally?

Этот вопрос нацелен на то, как JVM может гарантировать выполнение блока finally (если JVM не сбой и поток не прерывается или не выходит).

Подпрашиваемый вопросом интервью, я пытаюсь понять, как JVM может обеспечить, чтобы блок finally был выполнен даже в странных обстоятельствах... Рассмотрим следующий код:

try{

    int[] someArray = new int[10];
    int invalid = someArray[10];
}
catch(IndexOutOfBoundsException e){

    throw new RuntimeException("Other Exception");
}
finally{

    //close open files or HTTP connections etc.
}


Хотя это может быть странным обстоятельством, блок finally по-прежнему гарантированно выполняется, несмотря на то, что Other Exception не рассматривается явно. Как JVM обрабатывает такие ситуации?

Мои мысли:

Из того, что я понимаю и прочитал до сих пор, когда встречается необработанное исключение, управление передается из текущего потока (к этому потоку ThreadGroup, я думаю). Может ли быть какое-то условие в ThreadGroup, которое проверяет окончательные блоки, которые должны выполняться? Единственное, о чем я могу думать, это, пожалуй, адрес блока finally где-то. Затем JVM делает goto, когда исключение обнаруживается и возвращается к исключению, когда блок finally завершил выполнение.

Может ли кто-нибудь объяснить, как этот процесс на самом деле происходит?

4b9b3361

Ответ 1

Скомпилируйте эту небольшую программу (я понял, что должен был использовать ваш пример, но это не имеет значения)

public static void main(String[] args) {
    try {
        Float s = Float.parseFloat("0.0327f");
    } finally {
        System.out.println("hello");
    }
}

Я использовал

>java -version 
java version "1.8.0-ea"  // should be same for 7
Java(TM) SE Runtime Environment (build 1.8.0-ea-b118)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b60, mixed mode)

И затем выполните

javac -v -c <fully qualified class name>

чтобы получить байт-код. Вы увидите что-то вроде

public static void main(java.lang.String[]);
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
    stack=2, locals=3, args_size=1
       0: ldc           #2                  // String 0.0327f
       2: invokestatic  #3                  // Method java/lang/Float.parseFloat:(Ljava/lang/String;)F
       5: invokestatic  #4                  // Method java/lang/Float.valueOf:(F)Ljava/lang/Float;
       8: astore_1
       9: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      12: ldc           #6                  // String hello
      14: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      17: goto          31
      20: astore_2
      21: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      24: ldc           #6                  // String hello
      26: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      29: aload_2
      30: athrow
      31: return
    Exception table:
       from    to  target type
           0     9    20   any
          20    21    20   any
    LineNumberTable:
      line 10: 0
      line 12: 9
      line 13: 17
      line 12: 20
      line 14: 31
    StackMapTable: number_of_entries = 2
         frame_type = 84 /* same_locals_1_stack_item */
        stack = [ class java/lang/Throwable ]
         frame_type = 10 /* same */

Вы заметите, что код внутри finally появляется дважды, один раз перед goto и один раз после. Вы также заметите Exception table, который указывает, к какому оператору нужно идти, если в какой-либо строке возникает исключение.

Итак, если какое-либо исключение происходит между оператором 0-9, перейдите к строке 20 и выполните все внутри finally, после goto. Если исключение не выполняется, выполните finally, а затем выполните goto, пропустив finally после goto.

Во всех случаях вы должны выполнить код внутри блока finally.

Другое исключение явно не обрабатывается

С блоком finally будет создана запись Exception table, которая будет обрабатывать любой тип Throwable.


Ниже приведен список инструкций байткода.

Ответ 2

Я считаю, что blog четко описывает внутреннее:

Если метод определил try-catch или исключение try-finally обработчик, тогда будет создана таблица исключений. Это содержит информация для каждого обработчика исключений или, наконец, блок, включающий диапазон, по которому применяется обработчик, какой тип исключения обрабатывается и где код обработчика.

Когда вызывается исключение JVM ищет соответствующий обработчик в текущем методе, если ни один обнаружено, что метод внезапно выскакивает текущий стек стека и исключение повторно выбрасывается в вызывающем методе (новый ток Рамка). Если обработчик исключений не найден до того, как все кадры были после этого поток прекращается. Это также может привести к JVM само завершение, если исключение выбрано в последнем не-демоне нить, например, если поток является основным потоком.

Наконец, обработчики исключений соответствуют всем типам исключений и поэтому всегда выполняются всякий раз, когда генерируется исключение. В случае, когда нет исключение бросается, блок finally все еще выполняется в конце метод, это достигается путем перехода к коду обработчика finally непосредственно перед выполнением оператора return.