una todas tipos propagacion mas manejo las lanzar excepciones excepcion ejemplos comunes clase arbol java performance exception

java - todas - ¿Qué parte de lanzar una excepción es costosa?



tipos de excepciones en java netbeans (6)

Esta parte de la pregunta ...

Otra forma de preguntar esto es, si hice una instancia de Exception y la lancé y la atrapé una y otra vez, ¿sería significativamente más rápido que crear una nueva Excepción cada vez que lanzo?

Parece preguntarse si crear una excepción y almacenarla en caché en algún lugar mejora el rendimiento. Si lo hace Es lo mismo que apagar la pila que se escribe en la creación de objetos porque ya se ha hecho.

Estos son los tiempos que tengo, por favor lea la advertencia después de esto ...

|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%)| | 16| 193| 251| 77 (%)| | 15| 390| 406| 96 (%)| | 14| 394| 401| 98 (%)| | 13| 381| 385| 99 (%)| | 12| 387| 370| 105 (%)| | 11| 368| 376| 98 (%)| | 10| 188| 192| 98 (%)| | 9| 193| 195| 99 (%)| | 8| 200| 188| 106 (%)| | 7| 187| 184| 102 (%)| | 6| 196| 200| 98 (%)| | 5| 197| 193| 102 (%)| | 4| 198| 190| 104 (%)| | 3| 193| 183| 105 (%)|

Por supuesto, el problema con esto es que su rastro de pila ahora apunta a dónde instanciaron el objeto, no desde dónde fue arrojado.

En Java, usar tirar / atrapar como parte de la lógica cuando en realidad no hay un error es generalmente una mala idea (en parte) porque lanzar y atrapar una excepción es costoso, y hacerlo muchas veces en un ciclo suele ser mucho más lento que otro estructuras de control que no implican lanzar excepciones.

Mi pregunta es, ¿es el costo incurrido en el lanzamiento / captura en sí, o al crear el objeto Excepción (ya que obtiene mucha información de tiempo de ejecución, incluida la pila de ejecución)?

En otras palabras, si lo hago

Exception e = new Exception();

pero no lo lance, ¿es eso la mayor parte del costo del lanzamiento, o el manejo de lanzar + atrapar es lo que es costoso?

No estoy preguntando si poner código en un bloque try / catch aumenta el costo de ejecutar ese código, estoy preguntando si capturar la Excepción es la parte costosa, o crear (llamar al constructor) la Excepción es la parte costosa .

Otra forma de preguntar esto es, si hice una instancia de Exception y la lancé y la atrapé una y otra vez, ¿sería significativamente más rápido que crear una nueva Excepción cada vez que lanzo?


Hay un buen artículo sobre Excepciones aquí.

http://shipilev.net/blog/2014/exceptional-performance/

La conclusión es que la construcción del rastro de la pila y el desbobinado de la pila son las partes caras. El siguiente código aprovecha una característica en 1.7 donde podemos activar y desactivar los seguimientos de pila. Entonces podemos usar esto para ver qué tipo de costos tienen diferentes escenarios

Los siguientes son tiempos para la creación de objetos solo. He agregado String aquí para que pueda ver que sin la pila escrita, casi no hay diferencia en la creación de un objeto JavaException y una String . Con la escritura en pila activada, la diferencia es dramática, es decir, al menos un orden de magnitud más lento.

Time to create million String objects: 41.41 (ms) Time to create million JavaException objects with stack: 608.89 (ms) Time to create million JavaException objects without stack: 43.50 (ms)

A continuación se muestra el tiempo que se tardó en regresar de un lanzamiento a una profundidad particular un millón de veces.

|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%)| | 16| 1428| 243| 588 (%)| | 15| 1763| 393| 449 (%)| | 14| 1746| 390| 448 (%)| | 13| 1703| 384| 443 (%)| | 12| 1697| 391| 434 (%)| | 11| 1707| 410| 416 (%)| | 10| 1226| 197| 622 (%)| | 9| 1242| 206| 603 (%)| | 8| 1251| 207| 604 (%)| | 7| 1213| 208| 583 (%)| | 6| 1164| 206| 565 (%)| | 5| 1134| 205| 553 (%)| | 4| 1106| 203| 545 (%)| | 3| 1043| 192| 543 (%)|

Lo siguiente es casi seguro una gran simplificación ...

Si tomamos una profundidad de 16 con la escritura de la pila activada, la creación de objetos lleva aproximadamente ~ 40% del tiempo, el seguimiento real de la pila representa la gran mayoría de esto. ~ El 93% de la creación de instancias del objeto JavaException se debe al seguimiento de la pila que se está tomando. Esto significa que desenrollar la pila en este caso lleva el otro 50% del tiempo.

Cuando desactivamos la pila, la creación de objetos de rastreo representa una fracción mucho menor, es decir, el 20%, y el desenrollado de la pila ahora representa el 80% del tiempo.

En ambos casos, el desbobinado de la pila ocupa una gran parte del tiempo total.

public class JavaException extends Exception { JavaException(String reason, int mode) { super(reason, null, false, false); } JavaException(String reason) { super(reason); } public static void main(String[] args) { int iterations = 1000000; long create_time_with = 0; long create_time_without = 0; long create_string = 0; for (int i = 0; i < iterations; i++) { long start = System.nanoTime(); JavaException jex = new JavaException("testing"); long stop = System.nanoTime(); create_time_with += stop - start; start = System.nanoTime(); JavaException jex2 = new JavaException("testing", 1); stop = System.nanoTime(); create_time_without += stop - start; start = System.nanoTime(); String str = new String("testing"); stop = System.nanoTime(); create_string += stop - start; } double interval_with = ((double)create_time_with)/1000000; double interval_without = ((double)create_time_without)/1000000; double interval_string = ((double)create_string)/1000000; System.out.printf("Time to create %d String objects: %.2f (ms)/n", iterations, interval_string); System.out.printf("Time to create %d JavaException objects with stack: %.2f (ms)/n", iterations, interval_with); System.out.printf("Time to create %d JavaException objects without stack: %.2f (ms)/n", iterations, interval_without); JavaException jex = new JavaException("testing"); int depth = 14; int i = depth; double[] with_stack = new double[20]; double[] without_stack = new double[20]; for(; i > 0 ; --i) { without_stack[i] = jex.timerLoop(i, iterations, 0)/1000000; with_stack[i] = jex.timerLoop(i, iterations, 1)/1000000; } i = depth; System.out.printf("|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%%)|/n"); for(; i > 0 ; --i) { double ratio = (with_stack[i] / (double) without_stack[i]) * 100; System.out.printf("|%5d| %14.0f| %15.0f| %2.0f (%%)| /n", i + 2, with_stack[i] , without_stack[i], ratio); //System.out.printf("%d/t%.2f (ms)/n", i, ratio); } } private int thrower(int i, int mode) throws JavaException { ExArg.time_start[i] = System.nanoTime(); if(mode == 0) { throw new JavaException("without stack", 1); } throw new JavaException("with stack"); } private int catcher1(int i, int mode) throws JavaException{ return this.stack_of_calls(i, mode); } private long timerLoop(int depth, int iterations, int mode) { for (int i = 0; i < iterations; i++) { try { this.catcher1(depth, mode); } catch (JavaException e) { ExArg.time_accum[depth] += (System.nanoTime() - ExArg.time_start[depth]); } } //long stop = System.nanoTime(); return ExArg.time_accum[depth]; } private int bad_method14(int i, int mode) throws JavaException { if(i > 0) { this.thrower(i, mode); } return i; } private int bad_method13(int i, int mode) throws JavaException { if(i == 13) { this.thrower(i, mode); } return bad_method14(i,mode); } private int bad_method12(int i, int mode) throws JavaException{ if(i == 12) { this.thrower(i, mode); } return bad_method13(i,mode); } private int bad_method11(int i, int mode) throws JavaException{ if(i == 11) { this.thrower(i, mode); } return bad_method12(i,mode); } private int bad_method10(int i, int mode) throws JavaException{ if(i == 10) { this.thrower(i, mode); } return bad_method11(i,mode); } private int bad_method9(int i, int mode) throws JavaException{ if(i == 9) { this.thrower(i, mode); } return bad_method10(i,mode); } private int bad_method8(int i, int mode) throws JavaException{ if(i == 8) { this.thrower(i, mode); } return bad_method9(i,mode); } private int bad_method7(int i, int mode) throws JavaException{ if(i == 7) { this.thrower(i, mode); } return bad_method8(i,mode); } private int bad_method6(int i, int mode) throws JavaException{ if(i == 6) { this.thrower(i, mode); } return bad_method7(i,mode); } private int bad_method5(int i, int mode) throws JavaException{ if(i == 5) { this.thrower(i, mode); } return bad_method6(i,mode); } private int bad_method4(int i, int mode) throws JavaException{ if(i == 4) { this.thrower(i, mode); } return bad_method5(i,mode); } protected int bad_method3(int i, int mode) throws JavaException{ if(i == 3) { this.thrower(i, mode); } return bad_method4(i,mode); } private int bad_method2(int i, int mode) throws JavaException{ if(i == 2) { this.thrower(i, mode); } return bad_method3(i,mode); } private int bad_method1(int i, int mode) throws JavaException{ if(i == 1) { this.thrower(i, mode); } return bad_method2(i,mode); } private int stack_of_calls(int i, int mode) throws JavaException{ if(i == 0) { this.thrower(i, mode); } return bad_method1(i,mode); } } class ExArg { public static long[] time_start; public static long[] time_accum; static { time_start = new long[20]; time_accum = new long[20]; }; }

Los marcos de pila en este ejemplo son pequeños en comparación con lo que normalmente encontraría.

Puedes echar un vistazo al bytecode usando javap

javap -c -v -constants JavaException.class

es decir, esto es para el método 4 ...

protected int bad_method3(int, int) throws JavaException; flags: ACC_PROTECTED Code: stack=3, locals=3, args_size=3 0: iload_1 1: iconst_3 2: if_icmpne 12 5: aload_0 6: iload_1 7: iload_2 8: invokespecial #6 // Method thrower:(II)I 11: pop 12: aload_0 13: iload_1 14: iload_2 15: invokespecial #17 // Method bad_method4:(II)I 18: ireturn LineNumberTable: line 63: 0 line 64: 12 StackMapTable: number_of_entries = 1 frame_type = 12 /* same */ Exceptions: throws JavaException


La creación de la Exception con un seguimiento de pila null toma casi tanto tiempo como el bloque de throw y try-catch juntos. Sin embargo, completar el seguimiento de la pila tarda en promedio 5 veces más .

Creé el siguiente punto de referencia para demostrar el impacto en el rendimiento. -Djava.compiler=NONE a la Configuración de ejecución para deshabilitar la optimización del compilador. Para medir el impacto de construir el seguimiento de la pila, amplié la clase Exception para aprovechar el constructor sin pila:

class NoStackException extends Exception{ public NoStackException() { super("",null,false,false); } }

El código de referencia es el siguiente:

public class ExceptionBenchmark { private static final int NUM_TRIES = 100000; public static void main(String[] args) { long throwCatchTime = 0, newExceptionTime = 0, newObjectTime = 0, noStackExceptionTime = 0; for (int i = 0; i < 30; i++) { throwCatchTime += throwCatchLoop(); newExceptionTime += newExceptionLoop(); newObjectTime += newObjectLoop(); noStackExceptionTime += newNoStackExceptionLoop(); } System.out.println("throwCatchTime = " + throwCatchTime / 30); System.out.println("newExceptionTime = " + newExceptionTime / 30); System.out.println("newStringTime = " + newObjectTime / 30); System.out.println("noStackExceptionTime = " + noStackExceptionTime / 30); } private static long throwCatchLoop() { Exception ex = new Exception(); //Instantiated here long start = System.currentTimeMillis(); for (int i = 0; i < NUM_TRIES; i++) { try { throw ex; //repeatedly thrown } catch (Exception e) { // do nothing } } long stop = System.currentTimeMillis(); return stop - start; } private static long newExceptionLoop() { long start = System.currentTimeMillis(); for (int i = 0; i < NUM_TRIES; i++) { Exception e = new Exception(); } long stop = System.currentTimeMillis(); return stop - start; } private static long newObjectLoop() { long start = System.currentTimeMillis(); for (int i = 0; i < NUM_TRIES; i++) { Object o = new Object(); } long stop = System.currentTimeMillis(); return stop - start; } private static long newNoStackExceptionLoop() { long start = System.currentTimeMillis(); for (int i = 0; i < NUM_TRIES; i++) { NoStackException e = new NoStackException(); } long stop = System.currentTimeMillis(); return stop - start; } }

Salida:

throwCatchTime = 19 newExceptionTime = 77 newObjectTime = 3 noStackExceptionTime = 15

Esto implica que crear una NoStackException es aproximadamente tan costoso como lanzar repetidamente la misma Exception . También muestra que crear una Exception y completar el seguimiento de la pila tarda aproximadamente 4 veces más.


La primera operación en la mayoría de los constructores de Throwable es completar el seguimiento de la pila, que es donde está la mayor parte del gasto.

Sin embargo, hay un constructor protegido con una bandera para deshabilitar el seguimiento de la pila. Este constructor también es accesible cuando se extiende Exception . Si crea un tipo de excepción personalizado, puede evitar la creación del seguimiento de la pila y obtener un mejor rendimiento a expensas de menos información.

Si crea una única excepción de cualquier tipo por medios normales, puede volver a lanzarla muchas veces sin la sobrecarga de completar el seguimiento de la pila. Sin embargo, su seguimiento de pila reflejará dónde se construyó, no dónde se arrojó en una instancia particular.

Las versiones actuales de Java hacen algunos intentos para optimizar la creación de seguimiento de pila. El código nativo se invoca para completar el seguimiento de la pila, que registra el seguimiento en una estructura nativa más ligera. Los objetos Java StackTraceElement correspondientes se crean perezosamente a partir de este registro solo cuando se getStackTrace() , printStackTrace() u otros métodos que requieren el rastreo.

Si elimina la generación de seguimiento de la pila, el otro costo principal es desenrollar la pila entre el lanzamiento y la captura. Cuantas menos tramas intermedias se encuentren antes de que se detecte la excepción, más rápido será.

Diseñe su programa para que las excepciones solo se produzcan en casos verdaderamente excepcionales, y las optimizaciones como estas son difíciles de justificar.


Usando la respuesta de @ AustinD como punto de partida, realicé algunos ajustes. Código en la parte inferior.

Además de agregar el caso en el que se lanza una instancia de Excepción repetidamente, también desactivé la optimización del compilador para que podamos obtener resultados de rendimiento precisos. -Djava.compiler=NONE a los argumentos de VM, según esta respuesta . (En eclipse, edite Ejecutar configuración → Argumentos para establecer este argumento de VM)

Los resultados:

new Exception + throw/catch = 643.5 new Exception only = 510.7 throw/catch only = 115.2 new String (benchmark) = 669.8

Por lo tanto, crear la excepción cuesta aproximadamente 5 veces más que lanzarlo y atraparlo. Suponiendo que el compilador no optimiza gran parte del costo.

A modo de comparación, aquí está la misma ejecución de prueba sin desactivar la optimización:

new Exception + throw/catch = 382.6 new Exception only = 379.5 throw/catch only = 0.3 new String (benchmark) = 15.6

Código:

public class ExceptionPerformanceTest { private static final int NUM_TRIES = 1000000; public static void main(String[] args) { double numIterations = 10; long exceptionPlusCatchTime = 0, excepTime = 0, strTime = 0, throwTime = 0; for (int i = 0; i < numIterations; i++) { exceptionPlusCatchTime += exceptionPlusCatchBlock(); excepTime += createException(); throwTime += catchBlock(); strTime += createString(); } System.out.println("new Exception + throw/catch = " + exceptionPlusCatchTime / numIterations); System.out.println("new Exception only = " + excepTime / numIterations); System.out.println("throw/catch only = " + throwTime / numIterations); System.out.println("new String (benchmark) = " + strTime / numIterations); } private static long exceptionPlusCatchBlock() { long start = System.currentTimeMillis(); for (int i = 0; i < NUM_TRIES; i++) { try { throw new Exception(); } catch (Exception e) { // do nothing } } long stop = System.currentTimeMillis(); return stop - start; } private static long createException() { long start = System.currentTimeMillis(); for (int i = 0; i < NUM_TRIES; i++) { Exception e = new Exception(); } long stop = System.currentTimeMillis(); return stop - start; } private static long createString() { long start = System.currentTimeMillis(); for (int i = 0; i < NUM_TRIES; i++) { Object o = new String("" + i); } long stop = System.currentTimeMillis(); return stop - start; } private static long catchBlock() { Exception ex = new Exception(); //Instantiated here long start = System.currentTimeMillis(); for (int i = 0; i < NUM_TRIES; i++) { try { throw ex; //repeatedly thrown } catch (Exception e) { // do nothing } } long stop = System.currentTimeMillis(); return stop - start; } }


Crear un objeto de excepción no es más costoso que crear otros objetos regulares. El costo principal se oculta en el método nativo fillInStackTrace que recorre la pila de llamadas y recopila toda la información requerida para construir un seguimiento de la pila: clases, nombres de métodos, números de línea, etc.

El mito sobre los altos costos de excepción proviene del hecho de que la mayoría de los constructores de fillInStackTrace llaman implícitamente a fillInStackTrace . Sin embargo, hay un constructor para crear un Throwable sin un seguimiento de pila. Le permite crear objetos arrojables que son muy rápidos para crear instancias. Otra forma de crear excepciones ligeras es anular fillInStackTrace .

¿Y qué hay de lanzar una excepción?
De hecho, depende de dónde se capture una excepción lanzada.

Si se detecta en el mismo método (o, más precisamente, en el mismo contexto, ya que el contexto puede incluir varios métodos debido a la alineación), entonces throw es tan rápido y simple como goto (por supuesto, después de la compilación JIT).

Sin embargo, si un bloque catch está en algún lugar más profundo de la pila, JVM necesita desenrollar los marcos de la pila, y esto puede llevar mucho más tiempo. Lleva incluso más tiempo, si hay bloques synchronized o métodos involucrados, porque desenrollar implica la liberación de monitores propiedad de los marcos de pila eliminados.

Podría confirmar las declaraciones anteriores con los puntos de referencia adecuados, pero afortunadamente no necesito hacer esto, ya que todos los aspectos ya están perfectamente cubiertos en la publicación del ingeniero de rendimiento de HotSpot, Alexey Shipilev: El rendimiento excepcional de Lil ''Exception .