java - sirve - ¿Cómo se detectan y tratan las excepciones en el nivel bajo(ensamblado)?
try catch java (4)
Le recomendaría una respuesta a una pregunta de desbordamiento de pila .
Para los programas que se ejecutan en estas máquinas, el código de byte java es el lenguaje de máquina. No hay "lenguaje ensamblador".
Tengo este codigo
try {
doSomething();
} catch (Exception e) {
e.printStackTrace();
}
¿Cómo será implementado realmente por el compilador? ¿Dónde está la verificación de la excepción que realmente se coloca en el código de ensamblaje generado?
Actualizar
Sé que el código anterior se traduce a bytecode . El bytecode solo traduce el try-catch a los correspondientes bloques del controlador de intentos. Estoy interesado en cómo será traducido al ensamblado y / o manejado por el jvm.
No tengo una respuesta clara para usted, pero le proporcionaré los pasos para obtener el ensamblaje y podrá analizarlo según su caso de uso.
- Asegúrese de que su método en el que está interesado esté compilado para el ensamblaje
- Usualmente uso 2 para los bucles para arruinar el análisis de escape y me aseguro de que mi código no esté marcado como NOP
- Asegúrese de que está ejecutando una compilación JVM de depuración o que haya creado el desensamblador HotSpot: instrucciones para construir en una Mac .
- Ejecute su programa con java -XX: + UnlockDiagnosticVMOptions -XX: + PrintAssembly
- Prepárate para ver algún código optimizado .
También hay una herramienta GUI para analizar y visualizar el archivo de registro del compilador JIT, se llama JITWatch .
Aquí está la clase que preparé para probar esto, probablemente un poco verbosa, pero obtuve myMethod y doSomething tanto para compilar como para ensamblar.
public class Question {
public static void main(String[] args) {
long result = 0;
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
result += myMethod();
}
}
System.out.println(result);
}
private static long myMethod() {
try {
return doSomething();
}
catch (Exception e) {
return 100;
}
}
private static long doSomething() {
if (System.currentTimeMillis() % 2 == 0)
return System.currentTimeMillis();
else
throw new RuntimeException();
}
}
Si entiendo su pregunta correctamente, el siguiente código
public class Example {
public static void main(String[] args) {
try {
otherMethod();
}
catch (Exception e) {}
try {
otherMethod();
someMethod();
}
catch (SQLException e) {}
catch (IOException e) {}
}
public static void someMethod() throws IOException {throw new IOException();}
public static void otherMethod() throws SQLException, IOException {}
}
produce el siguiente (extracto de la versión legible por humanos de) código de byte.
// main method
0: invokestatic #2 // Method otherMethod:()V
3: goto 7
6: astore_1
7: invokestatic #2 // Method otherMethod:()V
10: invokestatic #4 // Method someMethod:()V
13: goto 21
16: astore_1
17: goto 21
20: astore_1
21: return
Exception table:
from to target type
0 3 6 Class java/lang/Exception
7 13 16 Class java/sql/SQLException
7 13 20 Class java/io/IOException
Notarás la Exception table
. Esta construcción le indica a la máquina virtual que si ocurre una excepción de tipo de type
entre la instrucción de from
a to
, entonces debe goto
instrucción (desplazamiento) de target
. También le indica que empuje la Referencia de Exception
en la pila para que su valor se pueda copiar y vincular al parámetro en el bloque catch
.
También tienes esta pieza relacionada con la declaración de throw
anterior.
// someMethod method
0: new #6 // class java/io/IOException
3: dup
4: invokespecial #7 // Method java/io/IOException."<init>":()V
7: athrow
La instrucción athrow
hace lo siguiente.
emite un error o una excepción (observe que el resto de la pila se borra, dejando solo una referencia al Throwable)
El objeto de referencia debe ser de referencia de tipo y debe referirse a un objeto que sea una instancia de la clase Throwable o de una subclase de Throwable. Se extrae de la pila de operandos. El objeto de referencia se lanza al buscar en el método actual (§2.6) el primer controlador de excepciones que coincida con la clase de objeto de referencia, como lo indica el algoritmo en la sección 2.10.
Si se encuentra un controlador de excepciones que coincide con objectref, contiene la ubicación del código destinado a manejar esta excepción. El registro de PC se restablece en esa ubicación, la pila de operandos del marco actual se borra, la referencia de objeto se empuja hacia atrás en la pila de operandos y la ejecución continúa.
Si no se encuentra un controlador de excepciones coincidente en el marco actual, ese marco se abre. Si la trama actual representa una invocación de un método sincronizado, el monitor ingresado o reingresado en la invocación del método se sale como si se ejecutara una instrucción de monitorexit (§monitorexit). Finalmente, el marco de su invocador se restablece, si tal marco existe, y el objeto de referencia se vuelve a derribar. Si no existe tal marco, el hilo actual sale.
Por lo tanto, los cuadros de la pila siguen apareciendo hasta que se encuentra uno que puede manejar la excepción lanzada.
¿Cómo será implementado realmente por el compilador? ¿Dónde está la verificación de la excepción que realmente se coloca en el código de ensamblaje generado?
El compilador genera el bytecode arriba. No hay verificación de una excepción , solo instrucciones de código de byte. El athrow
le athrow
a la VM que realice la tarea de lo que llamamos lanzar una excepción , lo que dará como resultado que se haga estallar la pila, se busquen tablas de excepciones en el marco de pila actual, etc.
El costo del bloque try-catch
En términos generales, el bloque try
no agrega ningún código de comprobación de excepción al conjunto de resultados. Básicamente es un no-op siempre y cuando no se lance una excepción. Todo el trabajo lento se realiza por excepción lanzando código.
Cuando try-catch
se compila con JIT, se agrega una tabla de excepciones aparte del código. Asigna los rangos de direcciones donde puede ocurrir una excepción manejada a la dirección del controlador de excepciones correspondiente. Nota: estos no son los índices de código de bytes, sino las direcciones de memoria reales.
¿Cómo se lanzan las excepciones en HotSpot?
- Excepciones implícitas:
NullPointerException
yError
se detectan dentro del manejador de señales en respuesta a una falla de segmentación. -
ArrayIndexOutOfBoundsException
,ClassCastException
, etc. se comprueban explícitamente. Las comprobaciones correspondientes están incorporadas en el código compilado donde se realiza el acceso a la matriz. -
OutOfMemoryError
y todas las demás excepcionesOutOfMemoryError
desde el código nativo se verifican explícitamente cada vez que se realiza una transición de estado de hilo (vm-> java o native-> java). - Todas las excepciones de usuario lanzadas por el
athrow
deathrow
deathrow
. En una ruta rápida (cuando existe un controlador decatch
en el mismo marco), JIT compila en un simple salto. De lo contrario, se produce la desoptimización y el manejo de excepciones se realiza dentro del tiempo de ejecución de la máquina virtual.
Bueno, ''¿Cómo se atrapa la excepción a nivel de ensamblaje?''
De ninguna manera.
Quiero decir, las excepciones generalmente no se detectan a nivel de ensamblaje: todo lo pesado (desplazamiento de pila, búsqueda de manejadores, desoptimización, desbloqueo de monitores, etc.) se realiza en tiempo de ejecución de VM, es decir, en código C.