java recursion jvm stack-overflow try-finally

java - Manejo de error de desbordamiento de pila en el bloque final



recursion jvm (5)

Pero, ¿cómo se está ejecutando la función recursiva en el bloque finally ya que enfrentamos un StackOverflowError?

Si intenta manejar un StackOverflowError , solo dará como resultado StackOverflowErrors posteriores, ya que está tratando de realizar más ejecuciones en una pila ya completa. No intente atrapar, use esta información para estructurar mejor su código. Este es el punto de una excepción sin marcar en java. Si no está familiarizado con los diferentes tipos de excepciones ...

Excepciones controladas

Las excepciones comprobadas son excepciones que se verifican en tiempo de compilación y representan las condiciones que deben manejarse (a través de una declaración try-catch) ya que está fuera del control del programa. Por ejemplo, si desea llamar a Thread.sleep() cualquier lugar dentro de un hilo determinado, se le pedirá que maneje una posible InterruptedException que podría ocurrir si el hilo es interrumpido por un hilo diferente. Dado que se conoce la posibilidad de que esto ocurra en el momento de la compilación, usted como programador es requerido para manejar este caso.

Excepciones no verificadas

La clase java.lang.Throwable dice ...

A los efectos de la verificación de excepciones en tiempo de compilación, Throwable y cualquier subclase de Throwable que no sea también una subclase de RuntimeException o Error se consideran excepciones comprobadas.

Como StackOverflowError es una subclase de Error , no se considera una excepción comprobada y, por lo tanto, está "desmarcada". Las excepciones no revisadas a menudo surgen debido a errores de programación, por ejemplo, en su caso, la terminación incorrecta de una recursión infinita . Podrían surgir otras excepciones no comprobadas si intenta acceder a algún miembro de una variable nula o un índice no válido en una matriz. Estas excepciones son casi imposibles de verificar en tiempo de compilación y se usan con más frecuencia para mostrarle al programador un error en su código.

Otras lecturas

Excepciones de Java

Excepciones marcadas frente a no comprobadas

Tengo un programa en Java, que se ejecuta infinitas veces.

Código de programa:

void asd() { try { //inside try block System.out.println("Inside try !!!"); asd(); } finally { //inside finally System.out.println("Inside finally !!!"); asd(); } }

SALIDA: este programa se ejecuta infinitamente, imprimiendo constantemente ambos sysouts.

Mi pregunta: en algún momento, comienza a arrojar StackOverflowErrors desde el bloque try y llega al bloque finally, donde llamamos nuevamente a esta función recursivamente. Pero, ¿cómo se está ejecutando la función recursiva en el bloque finally ya que enfrentamos un StackOverflowError?

¿Cómo maneja la JVM esta situación? ¿Ocurrirá el mismo comportamiento si obtenemos OutOfMemoryErrors también?


El problema es que tu ejemplo de programa es patológico. No funcionará, y no puede funcionar.

Pero ¿cómo se ejecuta la función recursiva en el bloque finally ya que enfrentamos Error ?

Hay una secuencia bastante complicada de llamadas en curso. Supongamos que la pila puede contener 3 cuadros. Una "ejecución manual" nos da una secuencia de llamadas / instantáneas de pila de llamadas de la siguiente manera:

asd() asd() > try asd() > try > asd() asd() > try > asd() > try asd() > try > asd() > try > asd() // ! asd() > try > asd() > finally asd() > try > asd() > finally > asd() // ! asd() > finally asd() > finally > asd() asd() > finally > asd() > try asd() > finally > asd() > try > asd() // ! asd() > finally > asd() > finally asd() > finally > asd() > finally > asd() // ! END

Como puede ver con una pila de profundidad 3, hicimos 7 llamadas, 4 de las cuales fallaron con un desbordamiento de la pila. Si realiza la ejecución manual para una pila de profundidad 4, obtendrá 15 llamadas, 5 => 31. El patrón es N => 2**N - 1 calls .

En su caso, la pila predeterminada podrá acomodar cientos o incluso miles de llamadas recursivas.

Diga N = 100. 2 ** 100 es una gran cantidad de llamadas. No es infinito, pero probablemente estarás muerto antes de que el programa termine.

¿Cómo maneja la JVM esta situación?

Como anteriormente. La JVM no está haciendo nada especial. El comportamiento del "bucle infinito efectivo" se debe exclusivamente a la forma en que se escribe su programa.

¿Ocurrirá el mismo comportamiento si obtenemos OutOfMemoryError s también?

Erm ... depende de tu programa. Pero estoy seguro de que podría inventar un programa de ejemplo que exhibió un patrón de comportamiento similar.


Obtendrá el mismo error cuando llame a asd en el bloque finally - necesita dejar que los recuadros asd en la pila vuelvan / aparezcan para resolver el error


Supongamos que el programa está ejecutando el método asd() y el espacio de la pila está a punto de terminar. Supongamos también que, en lugar de "inside try" y "inside finally", el método imprime un contador que indica qué tan lejos está la pila:

void asd(int i){ try{ //inside try block System.out.print(i); System.out.println("t"); asd(i+1); } finally{ //inside finally System.out.print(i); System.out.println("f"); asd(i+1); } }

}

Ahora esto es lo que hace el programa cuando está a punto de quedarse sin espacio en la pila, cuando i sea ​​9154.

La llamada a println("t") genera los caracteres y luego llama al método println (). Esto hace que el programa se quede sin espacio de pila, por lo que la ejecución se mueve al bloque finally . La llamada para imprimir allí se queda sin espacio de pila al imprimir la nueva línea. El error se lanza de nuevo, y la ejecución avanza al finally en el marco de activación por encima de la llamada al método actual. Esto hace que el programa imprima f por segunda vez, y como hemos sacado un marco de activación de la pila, esta llamada ahora se completa normalmente e imprime una nueva línea. El programa ha dado hasta ahora este resultado:

... 9152t 9153t 9154t9154f9153f // notice we jumped to i=1953

Ahora, el método se llama a sí mismo nuevamente, ahora desde el bloque finally. La situación con el espacio de pila es como antes, por lo que el programa se ejecuta como arriba, excepto que, como estábamos en un bloque finally para la llamada a método de i = 1953, la ejecución del programa termina en el bloque finally de la llamada al método de i = 1952:

9154t9154f9152f

El bloque finally de i = 9152 vuelve a llamar a asd , pasando i = 9153, y dado que ahora hay suficiente espacio de pila para imprimir una línea completa, el método genera el try-block:

9153t

y luego continúa llamándose a sí mismo, y en esta llamada terminará quedándose sin espacio de pila de nuevo, dando el resultado:

9154t9154f9153f

... y el resto de la salida se puede explicar de manera similar:

9154t9154f9151f 9152t 9153t 9154t9154f9153f 9154t9154f9152f 9153t 9154t9154f9153f 9154t9154f9150f 9151t 9152t 9153t ...

Lo que es importante notar es:

  1. El bloque finally se ejecuta incluso en el caso de Error .
  2. Un programa que se ha topado con un Error puede estar en un estado impredecible. Aquí, el único efecto observable es el hecho de que println no println un salto de línea. En un programa más complejo, esto podría significar que no puede confiar en el estado de cualquier cosa en la que el programa haya estado trabajando, y que lo más seguro es rescatar por completo.

Los errores, es decir, OutOfMemoryError, Error, etc. no están destinados a ser manejados. Dejan JVM en estado indefinido, nada está garantizado. En este punto, su aplicación simplemente debe finalizar y debe solucionar el problema que condujo a esto.

Su aplicación no debe tratar de manejar errores O ejecutar después de que uno haya sucedido. Si llama a la función recursiva en este punto, entonces usted es el único culpable. El resultado no es predecible

Consulte "11.1.1. El tipo de excepciones": http://docs.oracle.com/javase/specs/jls/se7/html/jls-11.html