Eche un vistazo a los siguientes dos métodos:
public static void foo() {
try {
foo();
} finally {
foo();
}
}
public static void bar() {
bar();
}
Ejecutar bar()
claramente da como resultado a StackOverflowError
, pero foo()
no lo hace (el programa parece ejecutarse indefinidamente). ¿Porqué es eso?
java
recursion
stack-overflow
try-finally
arshajii
fuente
fuente
finally
cláusula se propagarán al siguiente nivel. Pero no aguantes la respiración; el número de pasos dados será de aproximadamente 2 a la (profundidad máxima de la pila) y el lanzamiento de excepciones tampoco es exactamente barato.bar()
.Respuestas:
No funciona para siempre. Cada desbordamiento de pila hace que el código se mueva al bloque finalmente. El problema es que tomará mucho, mucho tiempo. El orden de tiempo es O (2 ^ N) donde N es la profundidad máxima de la pila.
Imagina que la profundidad máxima es 5
Para trabajar cada nivel en el bloque finalmente, tome el doble de tiempo y la profundidad de la pila podría ser de 10,000 o más. Si puede hacer 10,000,000 de llamadas por segundo, esto tomará 10 ^ 3003 segundos o más que la edad del universo.
fuente
-Xss
, obtengo una profundidad de [150 - 210], por lo que 2 ^ n termina siendo un número de dígitos [47 - 65]. No voy a esperar tanto, eso es lo suficientemente cercano al infinito para mí.foo
finalmente termina, ¿dará como resultado unStackOverflowError
?Cuando obtiene una excepción de la invocación de
foo()
dentro detry
, llamafoo()
desdefinally
y comienza a recurrir nuevamente. Cuando eso causa otra excepción, llamarásfoo()
desde otro interiorfinally()
, y así sucesivamente casi hasta el infinito .fuente
foo()
puede llamar finalmente después de una SOE?foo()
invocación e invocaráfoo()
en elfinally
bloque de sufoo()
invocación actual .Intenta ejecutar el siguiente código:
Encontrará que el bloque finalmente se ejecuta antes de lanzar una Excepción hasta el nivel superior. (Salida:
Esto tiene sentido, ya que finalmente se llama justo antes de salir del método. Esto significa, sin embargo, que una vez que obtenga eso primero
StackOverflowError
, intentará lanzarlo, pero finalmente debe ejecutarse primero, por lo que se ejecutafoo()
nuevamente, lo que genera otro desbordamiento de la pila y, como tal, finalmente se ejecuta nuevamente. Esto sigue sucediendo para siempre, por lo que la excepción nunca se imprime.Sin embargo, en su método de barra, tan pronto como se produce la excepción, se lanza directamente al nivel superior y se imprimirá
fuente
En un esfuerzo por proporcionar evidencia razonable de que esto eventualmente terminará, ofrezco el siguiente código bastante sin sentido. Nota: Java NO es mi lenguaje, por cualquier tramo de la imaginación más vívida. Lo propongo solo para apoyar la respuesta de Peter, que es la respuesta correcta a la pregunta.
Esto intenta simular las condiciones de lo que sucede cuando una invocación NO puede suceder porque introduciría un desbordamiento de pila. Me parece que lo más difícil que la gente no comprende es que la invocación no ocurre cuando no puede suceder.
La salida de este pequeño montón de cosas sin sentido es la siguiente, y la excepción real atrapada puede ser una sorpresa; Ah, y 32 llamadas de prueba (2 ^ 5), lo cual es completamente esperado:
fuente
Aprenda a rastrear su programa:
Esta es la salida que veo:
Como puede ver, el StackOverFlow se lanza en algunas capas arriba, por lo que puede realizar pasos de recursión adicionales hasta que encuentre otra excepción, y así sucesivamente. Este es un "bucle" infinito.
fuente
foo
segunda vez, en elfinally
bloque, ya no está en untry
. Entonces, si bien volverá a bajar por la pila y creará más desbordamientos de pila una vez, la segunda vez simplemente volverá a lanzar el error producido por la segunda llamada afoo
, en lugar de volver a profundizar.El programa simplemente parece ejecutarse para siempre; en realidad termina, pero lleva exponencialmente más tiempo cuanto más espacio de pila tenga. Para demostrar que termina, escribí un programa que primero agota la mayor parte del espacio de pila disponible, luego llama
foo
y finalmente escribe un rastro de lo que sucedió:El código:
¡Puedes probarlo en línea! (Algunas ejecuciones pueden llamar
foo
más o menos veces que otras)fuente