299 votos

Cuán lentos son excepciones Java?

Pregunta: Es el manejo de excepciones en Java realmente lento?

La sabiduría convencional, así como una gran cantidad de resultados de Google, dice que la excepcional lógica no debe ser utilizado para el normal flujo de un programa en Java. Dos razones son dadas usualmente, 1) es realmente lento - incluso de un orden de magnitud más lento de lo normal código (las razones dadas variar), y 2) desordenado porque las personas sólo esperan que los errores se manejan en excepcionales código. Esta pregunta es acerca de #1.

Como ejemplo, esta página describe Java manejo de excepciones como "muy lento" y que se refiere a la lentitud a la creación de la excepción a la cadena de mensaje - "esta cadena se utiliza en la creación del objeto de excepción que se lanza. Esto no es rápido". El artículo Eficaces de Manejo de excepciones en Java dice que "la razón de esto es debido a la creación del objeto de los aspectos de manejo de excepciones, lo que lo hace lanzando excepciones inherentemente lento". Otra razón es que el seguimiento de la pila de generación es lo que lo frena.

Mi prueba (utilizando Java 1.6.0_07, Java HotSpot 10.0, en 32 bits de Linux), indica que el manejo de excepciones no es más lento de lo normal código. Traté de ejecución de un método en un bucle que se ejecuta el código. Al final del método, yo uso un valor booleano para indicar si la devolución o el tiro. De esta manera el proceso es el mismo. He intentado correr los métodos en diferentes órdenes y un promedio de mis tiempos de prueba, pensando que puede haber sido la JVM calentamiento. En todas mis pruebas, el tiro fue al menos tan rápido como el regreso, si no más rápido (hasta el 3,1% más rápido). Estoy completamente abierto a la posibilidad de que mis pruebas estaban equivocadas, pero no he visto nada por ahí en el camino del ejemplo de código, prueba de comparaciones, o los resultados en el último año o dos que muestran el manejo de excepciones en Java para ser realmente lento.

Lo que me llevan por este camino fue una API que necesitaba usar que lanzó excepciones como parte normal de la lógica de control. Quería corregir en su uso, pero ahora yo no puedo ser capaz. Yo en lugar de tener a la alabanza en su visión de futuro?

En el papel el Eficiente manejo de excepciones en Java en tiempo de compilación, los autores sugieren que la presencia de los controladores de excepciones solo, incluso si no se producen excepciones, es suficiente para evitar que el compilador JIT de optimizar el código correctamente, por lo que se ralentice. Yo no he probado esta teoría, sin embargo.

224voto

Mecki Puntos 35351

Depende de cómo las excepciones son implementadas. La forma más sencilla es utilizando setjmp y longjmp. Eso significa que todos los registros de la CPU se escriben en la pila (que ya lleva algún tiempo) y posiblemente algunos otros datos debe ser creada... todo esto ya sucede en la instrucción try. La sentencia throw necesita para relajarse en la pila y restaurar los valores de todos los registros (y otros posibles valores en la VM). Así que intenta tirar son igual de lento, y que es bastante lento, sin embargo, si no se emite ninguna excepción, saliendo del bloque try no toma ningún momento y por ningún motivo en la mayoría de los casos (como todo lo que se pone en la pila que se limpia automáticamente si existe el método).

Sol y otros reconocieron que este es posiblemente subóptima y, por supuesto, VMs más y más rápido a través del tiempo. No hay otra forma de implementar excepciones, lo que hace que trate a sí misma velocidad del rayo (en realidad no pasa nada por probar a todos en general todo lo que tiene que suceder ya está hecho cuando se carga la clase por el VM) y hace que tiro no es tan lento. No sé que JVM utiliza esta nueva, mejor técnica...

...pero, ¿está escrito en Java por lo que su código sólo se ejecuta en una JVM en un sistema específico? Ya que si se puede ejecutar en cualquier plataforma o cualquier otra versión de la JVM (posiblemente de cualquier otro proveedor), quien dice que también utilizan la aplicación rápida? La velocidad a la que uno es más complicado que el que es lento y no fácilmente posible en todos los sistemas. Usted desea permanecer portátil? Entonces no dependen de las excepciones de ser rápido.

También hace una gran diferencia en lo que haces dentro de un bloque try. Si abre un bloque try y nunca llamar a cualquier método de dentro de este bloque try, el bloque try será ultra rápido, como el JIT puede tratar de un tiro como un simple goto. No necesita guardar en la pila de estado ni tampoco necesita para relajarse de la pila si se produce una excepción (que sólo necesita saltar a la captura de los controladores). Sin embargo, esto no es lo que se suele hacer. Normalmente se abre un bloque try y, a continuación, llamar a un método que podría lanzar una excepción, ¿verdad? E incluso si usted sólo tiene que utilizar el bloque try dentro de su método, ¿qué tipo de método va a ser esto, que no llame a cualquier otro método? Se acaba de calcular un número? Entonces, ¿para qué necesitan las excepciones? Hay mucho más elegante la forma de regular el flujo de un programa. Para casi cualquier cosa, pero la matemática simple, usted tendrá que llamar a un método externo y esto ya destruye la ventaja de un local de bloque try.

Consulte el siguiente código de prueba:

public class Test {
    int value;


    public int getValue() {
    	return value;
    }

    public void reset() {
    	value = 0;
    }

    // Calculates without exception
    public void method1(int i) {
    	value = ((value + i) / i) << 1;
    	// Will never be true
    	if ((i & 0xFFFFFFF) == 1000000000) {
    		System.out.println("You'll never see this!");
    	}
    }

    // Could in theory throw one, but never will
    public void method2(int i) throws Exception {
    	value = ((value + i) / i) << 1;
    	// Will never be true
    	if ((i & 0xFFFFFFF) == 1000000000) {
    		throw new Exception();
    	}
    }

    // This one will regularly throw one
    public void method3(int i) throws Exception {
    	value = ((value + i) / i) << 1;
    	// i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
    	// an AND operation between two integers. The size of the number plays
    	// no role. AND on 32 BIT always ANDs all 32 bits
    	if ((i & 0x1) == 1) {
    		throw new Exception();
    	}
    }

    public static void main(String[] args) {
    	int i;
    	long l;
    	Test t = new Test();

    	l = System.currentTimeMillis();
    	t.reset();
    	for (i = 1; i < 100000000; i++) {
    		t.method1(i);
    	}
    	l = System.currentTimeMillis() - l;
    	System.out.println(
    		"method1 took " + l + " ms, result was " + t.getValue()
    	);

    	l = System.currentTimeMillis();
    	t.reset();
    	for (i = 1; i < 100000000; i++) {
    		try {
    			t.method2(i);
    		} catch (Exception e) {
    			System.out.println("You'll never see this!");
    		}
    	}
    	l = System.currentTimeMillis() - l;
    	System.out.println(
    		"method2 took " + l + " ms, result was " + t.getValue()
    	);

    	l = System.currentTimeMillis();
    	t.reset();
    	for (i = 1; i < 100000000; i++) {
    		try {
    			t.method3(i);
    		} catch (Exception e) {
    			// Do nothing here, as we will get here
    		}
    	}
    	l = System.currentTimeMillis() - l;
    	System.out.println(
    		"method3 took " + l + " ms, result was " + t.getValue()
    	);
    }
}

Resultado:

method1 took 972 ms, result was 2
method2 took 1003 ms, result was 2
method3 took 66716 ms, result was 2

Como se puede ver, ya que el bloque try que hizo todo lo que se ejecuta más lentamente. El bloque catch mató todo y lo hizo 66 veces más lento!

Como he dicho, el resultado no va a ser tan malo si usted pone try/catch y throw todos dentro de un mismo método (method3), pero este es un caso especial de la optimización JIT yo no podría confiar. Y aun cuando el uso de esta optimización, el lanzamiento es todavía muy lento. Así que no sé lo que usted está tratando de hacer aquí, pero definitivamente hay una mejor manera de hacerlo que usando try/catch/tiro.

148voto

Hot Licks Puntos 25075

FYI, me extendió el experimento que Mecki hizo:

method1 took 1733 ms, result was 2
method2 took 1248 ms, result was 2
method3 took 83997 ms, result was 2
method4 took 1692 ms, result was 2
method5 took 60946 ms, result was 2
method6 took 25746 ms, result was 2

Los 3 primeros son los mismos que Mecki (mi laptop es obviamente más lento).

method4 es idéntica a method3 excepto que crea un new Integer(1) más que en hacer throw new Exception().

method5 es como method3 salvo que se crea la new Exception() sin tirar.

method6 es como method3 salvo que se produce una pre-creados excepción (una variable de instancia), en lugar de crear uno nuevo.

En Java la mayor parte del costo de producir una excepción es el tiempo dedicado a la recopilación de la traza de la pila, que se produce cuando la excepción se ha creado el objeto. El costo real de lanzar la excepción, mientras que los grandes, es considerablemente menor que el costo de la creación de la excepción.

28voto

Fuwjax Puntos 581

Mi respuesta, lamentablemente, es demasiado largo para publicar aquí. Permítanme resumir aquí y consulte a http://blog.fuwjax.org/2011/04/how-slow-are-java-exceptions/ para los detalles más irrelevantes.

La verdadera cuestión aquí no es "Cuán lentos son " fallas reportadas como excepciones " en comparación con el código de los que nunca falla"?" como respuesta aceptado que pudiera usted creer. Vez la pregunta debería ser "¿Cómo lentos son " fallas reportadas como excepciones " en comparación con los fallos informado de otras maneras?" Generalmente, las otras dos formas de reportar los fallos están con centinela de valores o con los objetos de resultado.

Centinela de valores son un intento de devolver una clase en el caso de éxito y otro en caso de fallo. Puedes pensar que es casi como volver a una excepción en vez de tirar de uno. Esto requiere de una compartida de los padres de clase con el éxito de objeto y, a continuación, hacer un "instanceof" de verificación y un par de moldes para conseguir el éxito o el fracaso de la información.

Resulta que en el riesgo de tipo de seguridad, Centinela valores son más rápidas que las excepciones, pero sólo por un factor de aproximadamente 2x. Ahora, que puede parecer mucho, pero que 2x sólo cubre el costo de la implementación de la diferencia. En la práctica, el factor es mucho menor ya que nuestros métodos que puede fallar son mucho más interesantes que un par de operadores aritméticos como en el código de ejemplo en otros lugares en esta página.

Los Objetos de resultado en el otro lado no sacrifique la seguridad de tipos. Ellos envuelven el éxito y el fracaso de la información en una sola clase. Así que en lugar de "instanceof" que proporcionan una "isSuccess()" y getters para el éxito y el fracaso de los objetos. Sin embargo, los objetos de resultado aproximadamente 2x más lento que el uso de excepciones. Resulta que la creación de un nuevo objeto cada vez que es mucho más caro que el de lanzar una excepción a veces.

Encima de eso, las excepciones son el lenguaje suministra forma de indicar que un método puede fallar. No hay otra forma de decir de sólo el API, que los métodos que se espera para siempre (en su mayoría) de trabajo y que se espera que el informe de fallo.

Las excepciones son más seguros que los centinelas, más rápido que los objetos de resultado, y menos sorprendente que cualquiera de los dos. No estoy sugiriendo que los try/catch reemplazar if/else, pero las excepciones son la manera correcta de informe de fracaso, incluso en la lógica de negocio.

Dicho esto, me gustaría señalar que los dos modos más frecuentes de sustancialmente afectar el rendimiento que he conocido son la creación de objetos innecesarios y bucles anidados. Si usted tiene una opción entre la creación de una excepción o no de crear una excepción, no crear la excepción. Si usted tiene una opción entre la creación de una excepción a veces o la creación de otro objeto todo el tiempo, a continuación, crear la excepción.

7voto

Lars Westergren Puntos 1362

Creo que el primer artículo se refieren al acto de atravesar la pila de llamadas y la creación de una traza de la pila como la pieza cara, y mientras que el segundo artículo no lo dice, creo que es la parte más costosa de la creación del objeto. Juan de la Rosa tiene un artículo donde se describe las diferentes técnicas para acelerar las excepciones. (Preallocating y la reutilización de una excepción, excepciones, sin trazas de pila, etc.)

Pero aún así, creo que este debe ser considerada sólo como un mal necesario, un último recurso. Johns razón para hacer esto es para emular las características en otros idiomas que no son (aún) no disponible en la JVM. Usted NO debe entrar en el hábito de utilizar excepciones para el control de flujo. Especialmente por razones de rendimiento! Como usted mismo menciona en #2, el riesgo de enmascaramiento de fallos graves en el código de esta manera, y será más difícil de mantener para los nuevos programadores.

Microbenchmarks en Java son sorprendentemente difícil hacerlo bien (me han dicho), especialmente cuando usted consigue en JIT territorio, así que realmente duda de que el uso de las excepciones es más rápido que el de "volver" en la vida real. Por ejemplo, sospecho que usted tiene en algún lugar entre el 2 y el 5 marcos de pila en su prueba? Ahora imagina tu código va a ser invocada por un componente JSF desplegado por JBoss. Ahora usted puede tener un seguimiento de pila, que es varias páginas de largo.

Tal vez usted pueda publicar su código de prueba?

6voto

James Schek Puntos 11070

He hecho algunas pruebas de rendimiento con JVM 1.5 y el uso de las excepciones era por lo menos 2x más lento. En promedio: el tiempo de Ejecución en un extemadamente pequeño método más que triplicado (3x) con excepciones. Un extemadamente pequeño lazo que había para capturar la excepción vio un 2x aumentar en el tiempo.

He visto un número similar en el código de producción, así como los puntos de referencia.

Las excepciones deben definitivamente NO se utilizan para nada de lo que se llama con frecuencia. Lanzando miles de excepciones un segundo podría causar un gran cuello de botella.

Por ejemplo, el uso de "Integer.ParseInt(...)" para encontrar todos los malos valores en un archivo de texto muy grandes, muy mala idea. (Yo he visto a este método de utilidad matar el rendimiento en la producción de código)

El uso de una excepción a informe de malo en que un usuario GUI forma, probablemente no es tan malo desde un punto de vista del rendimiento.

Si o no es una buena práctica de diseño, me gustaría ir con la regla: si el error es normal/se espera que, a continuación, utilice un valor de retorno. Si es anormal, uso una excepción. Por ejemplo: la lectura de las entradas del usuario, mal valores son normales-usar un código de error. Al pasar un valor a un interno de la función de utilidad, mala valores debe ser filtrada por el código de llamada : utiliza una excepción.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X