tabla que programa maquina interprete hacer funciona ejemplos como codigo caracteristicas java jvm javac try-catch-finally try-finally

programa - ¿Por qué la copia del compilador de Java finalmente se bloquea?



que es jvm (2)

Alineando finalmente los bloques

La pregunta que hizo su pregunta ha sido analizada en parte en: http://devblog.guidewire.com/2009/10/22/compiling-trycatchfinally-on-the-jvm/

La publicación mostrará un ejemplo interesante, así como información como (cita):

los bloques finalmente se implementan al alinear el código finalmente en todas las salidas posibles de los bloques try o asociados catch, envolviendo todo en esencialmente un bloque "catch (Throwable)" que vuelve a generar la excepción cuando finaliza, y luego ajustando la tabla de excepciones como que las cláusulas catch se saltan las declaraciones finalmente en línea. Eh (Pequeña advertencia: antes del compilador 1.6, aparentemente, finalmente las declaraciones usaban sub-rutinas en lugar de un código completo en línea. Pero solo nos preocupa el 1.6 en este punto, así que eso es lo que se aplica a).

La instrucción JSR y en línea finalmente

Hay diferentes opiniones acerca de por qué se utiliza la inscripción, aunque todavía no he encontrado una definitiva de un documento oficial o fuente.

Hay las siguientes 3 explicaciones:

Sin ventajas de oferta - más problemas:

Algunos creen que finalmente se usa la alineación porque JSR / RET no ofreció grandes ventajas, como la cita de ¿Qué compiladores de Java usan la instrucción jsr, y para qué?

El mecanismo JSR / RET se usó originalmente para implementar finalmente los bloques. Sin embargo, decidieron que el ahorro en el tamaño del código no valía la complejidad adicional y se fue reduciendo gradualmente.

Problemas con la verificación utilizando tablas de mapa de pila:

Otra explicación posible ha sido propuesta en los comentarios de @ jeffrey-bosboom, a quienes cito a continuación:

javac solía usar jsr (subrutina de salto) para escribir finalmente el código una vez, pero había algunos problemas relacionados con la nueva verificación al usar tablas de mapas de pila. Supongo que volvieron a clonar el código solo porque era lo más fácil de hacer.

Tener que mantener los bits sucios de subrutinas:

Un intercambio interesante en los comentarios de la pregunta ¿Qué compiladores Java utilizan la instrucción jsr y para qué? los puntos que JSR y las subrutinas "añadieron una complejidad adicional al tener que mantener una pila de bits sucios para las variables locales".

Debajo del intercambio:

@ paj28: ¿El JSR habría planteado tales dificultades si solo pudiera llamar "subrutinas" declaradas, cada una de las cuales solo podría ingresarse al comienzo, solo se podría llamar desde otra subrutina, y solo podría salir por medio de la repetición o la finalización abrupta ( volver o tirar)? Duplicar el código en los bloques finalmente parece realmente feo, especialmente porque la limpieza finalmente relacionada puede invocar bloques de prueba anidados. - Supercat 28 de enero de 14 a las 23:18

@supercat, la mayoría de eso ya es cierto. Las subrutinas solo pueden ingresarse desde el principio, solo pueden regresar desde un lugar y solo pueden llamarse desde una única subrutina. La complejidad proviene del hecho de que debe mantener una pila de bits sucios para las variables locales y, al regresar, debe realizar una combinación de tres vías. - Antimonio enero 28 ''14 a las 23:40

Al compilar el siguiente código con un simple bloque try/finally , el compilador de Java produce la salida a continuación (vista en el Visor de Bytecode de ASM):

Código:

try { System.out.println("Attempting to divide by zero..."); System.out.println(1 / 0); } finally { System.out.println("Finally..."); }

Bytecode:

TRYCATCHBLOCK L0 L1 L1 L0 LINENUMBER 10 L0 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; LDC "Attempting to divide by zero..." INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L2 LINENUMBER 11 L2 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ICONST_1 ICONST_0 IDIV INVOKEVIRTUAL java/io/PrintStream.println (I)V L3 LINENUMBER 12 L3 GOTO L4 L1 LINENUMBER 14 L1 FRAME SAME1 java/lang/Throwable ASTORE 1 L5 LINENUMBER 15 L5 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; LDC "Finally..." INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L6 LINENUMBER 16 L6 ALOAD 1 ATHROW L4 LINENUMBER 15 L4 FRAME SAME GETSTATIC java/lang/System.out : Ljava/io/PrintStream; LDC "Finally..." INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L7 LINENUMBER 17 L7 RETURN L8 LOCALVARIABLE args [Ljava/lang/String; L0 L8 0 MAXSTACK = 3 MAXLOCALS = 2

Al agregar un bloque catch en medio, me di cuenta de que el compilador copió el bloque final 3 veces (sin publicar el código de bytes nuevamente). Esto parece una pérdida de espacio en el archivo de clase. La copia tampoco parece estar limitada a un número máximo de instrucciones (similar a la forma en que funciona la integración), ya que incluso duplicó el bloque final cuando agregué más llamadas a System.out.println .

Sin embargo, el resultado de un compilador mío personalizado que utiliza un enfoque diferente para compilar el mismo código funciona exactamente igual cuando se ejecuta, pero requiere menos espacio al usar la instrucción GOTO :

public static main([Ljava/lang/String;)V // parameter args TRYCATCHBLOCK L0 L1 L1 L0 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; LDC "Attempting to divide by zero..." INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ICONST_1 ICONST_0 IDIV INVOKEVIRTUAL java/io/PrintStream.println (I)V GOTO L2 L1 FRAME SAME1 java/lang/Throwable POP L2 FRAME SAME GETSTATIC java/lang/System.out : Ljava/io/PrintStream; LDC "Finally..." INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L3 RETURN LOCALVARIABLE args [Ljava/lang/String; L0 L3 0 MAXSTACK = 3 MAXLOCALS = 1

¿Por qué el compilador de Java (o el compilador de Eclipse) copia el athrow de athrow bloque athrow varias veces, incluso utilizando athrow para volver a athrow excepciones, cuando se puede lograr la misma semántica utilizando goto ? ¿Esto es parte del proceso de optimización o mi compilador lo está haciendo mal?

(La salida en ambos casos es ...)

Attempting to divide by zero... Finally...


Compilando esto:

public static void main(String... args){ try { System.out.println("Attempting to divide by zero..."); System.out.println(1 / 0); }catch(Exception e){ System.out.println("Exception!"); } finally { System.out.println("Finally..."); } }

Y mirando el resultado de javap -v, el bloque finally se adjunta simplemente al final de cada sección que maneja una excepción (agregando el catch, se agrega un bloque finally en la línea 37, el de 49 es para java.lang sin marcar. Errores):

public static void main(java.lang.String...); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS Code: stack=3, locals=3, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Attempting to divide by zero... 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 11: iconst_1 12: iconst_0 13: idiv 14: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 17: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 20: ldc #6 // String Finally... 22: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 25: goto 59 28: astore_1 29: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 32: ldc #8 // String Exception! 34: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 37: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 40: ldc #6 // String Finally... 42: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 45: goto 59 48: astore_2 49: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 52: ldc #6 // String Finally... 54: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 57: aload_2 58: athrow 59: return Exception table: from to target type 0 17 28 Class java/lang/Exception 0 17 48 any 28 37 48 any

Parece que la implementación original de finalmente los bloques se parecía a lo que estás proponiendo, pero desde que Java 1.4.2 javac comenzó a integrar bloques finalmente, desde " Una evaluación de los decompiladores actuales de bytecode Java " [2009] de Hamilton & Danicic:

Muchos de los antiguos descompiladores esperan el uso de subrutinas para los bloques try-finally pero javac 1.4.2+ genera un código en línea en su lugar.

Una entrada de blog de 2006 que discute esto:

El código en las líneas 5-12 es idéntico al código en las líneas 19-26, que en realidad se traduce en la línea count ++. El bloque finalmente está claramente copiado.