java - propagacion - ¿Cómo garantiza la JVM la ejecución del bloque finally?
propagacion de excepciones en java (2)
Esta pregunta está dirigida a cómo la JVM puede garantizar la ejecución de un bloque finally (siempre que la JVM no se bloquee y el subproceso no se interrumpa ni salga).
Ante una pregunta de entrevista, estoy tratando de entender cómo la JVM es capaz de garantizar que se ejecute un bloque final incluso en circunstancias extrañas ... Considere el siguiente código:
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.
}
Si bien esto puede ser una circunstancia extraña, todavía se garantiza que el bloque finally se ejecute a pesar de que la Otra excepción no se maneje explícitamente. ¿Cómo maneja la JVM situaciones como esta?
Mis pensamientos:
Por lo que he entendido y he leído hasta ahora, cuando se encuentra una excepción no controlada, el control se transfiere desde el hilo actual (al ThreadGroup
ese hilo, creo). ¿Podría haber alguna disposición en el ThreadGroup
que verifique finalmente los bloques que deben ejecutarse? La única otra cosa en la que puedo pensar es tal vez la dirección del bloque final está almacenada en algún lugar. Luego, JVM hace un goto cuando se detecta una excepción y regresa a la excepción cuando el bloque finalmente ha finalizado su ejecución.
¿Alguien puede aclarar cómo sucede realmente este proceso?
Compila este pequeño programa (me di cuenta de que debería haber usado tu ejemplo, pero no hace ninguna diferencia)
public static void main(String[] args) {
try {
Float s = Float.parseFloat("0.0327f");
} finally {
System.out.println("hello");
}
}
solía
>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)
Y luego ejecutar
javac -v -c <fully qualified class name>
para obtener el bytecode. Verás algo como
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 */
Notarás que el código dentro de finally
aparece dos veces , una antes del goto
y otra después. También notará la Exception table
que especifica a qué declaración ir si se produce una excepción en alguna línea.
Entonces, si ocurre alguna excepción entre la instrucción 0-9, vaya a la línea 20 y ejecute todo dentro de finally
, después del goto
. Si no se produce ninguna excepción, ejecute finally
y luego ejecute el goto
saltando el finally
después del goto
.
En todos los casos, habrás ejecutado el código dentro del bloque finally
.
Otra excepción no está siendo manejada explícitamente
Con un bloque finally
, se creará una entrada en la Exception table
que manejará cualquier tipo de Throwable
.
Creo que este blog describe claramente lo interno:
Si un método ha definido un controlador de excepciones try-catch o try-finally, se creará una tabla de excepciones. Esto contiene información para cada controlador de excepciones o, finalmente, bloque, incluido el rango sobre el que se aplica el controlador, qué tipo de excepción se está manejando y dónde está el código del controlador.
Cuando se lanza una excepción, la JVM busca un controlador coincidente en el método actual, si no se encuentra ninguno, el método termina abriendo bruscamente el marco de pila actual y la excepción se vuelve a lanzar en el método de llamada (el nuevo marco actual). Si no se encuentra un controlador de excepciones antes de que se hayan hecho estallar todos los marcos, entonces se termina el hilo. Esto también puede hacer que la JVM finalice si se lanza la excepción en el último subproceso no daemon, por ejemplo, si el subproceso es el subproceso principal.
Finalmente, los controladores de excepciones coinciden con todos los tipos de excepciones y, por lo tanto, siempre se ejecutan cuando se lanza una excepción. En el caso de que no se produzca una excepción, se ejecuta un bloque finally al final de un método, esto se logra saltando al código del controlador finally inmediatamente antes de que se ejecute la instrucción return.