java - como - ¿Por qué es posible recuperar de StackOverflowError?
como usar stack overflow (4)
Cuando la pila se desborda y se lanza StackOverflowError
, el manejo habitual de las excepciones desenrolla la pila. Desenrollar la pila significa:
- abortar la ejecución de la función actualmente activa
- eliminar su marco de pila, continuar con la función de llamada
- abortar la ejecución de la persona que llama
- eliminar su marco de pila, continuar con la función de llamada
- y así...
... hasta que se atrape la excepción. Esto es normal (de hecho, necesario) e independiente de qué excepción se arroja y por qué. Como capturas la excepción fuera de la primera llamada a foo()
, los miles de foo
stack frames que llenaron la pila se han desenrollado y la mayor parte de la pila puede volver a utilizarse.
Me sorprende cómo es posible continuar la ejecución incluso después de que se haya producido un StackOverflowError
en Java.
Sé que StackOverflowError
es una subclase de la clase Error. La clase Error se decumenta como "una subclase de Throwable que indica problemas graves que una aplicación razonable no debería tratar de detectar".
Esto suena más como una recomendación que como una regla, subrayando que la captura de un error como StackOverflowError de hecho está permitida y depende de la razonabilidad del programador no hacerlo. Y mira, probé este código y termina normalmente.
public class Test
{
public static void main(String[] args)
{
try {
foo();
} catch (StackOverflowError e) {
bar();
}
System.out.println("normal termination");
}
private static void foo() {
System.out.println("foo");
foo();
}
private static void bar() {
System.out.println("bar");
}
}
¿Cómo puede ser esto? Creo que para el momento en que se lanza StackOverflowError, la pila debe estar tan llena que no haya espacio para llamar a otra función. ¿Se está ejecutando el bloque de manejo de errores en una pila diferente, o qué está sucediendo aquí?
Cuando se lanza Error, la pila está llena. Sin embargo, cuando se detecta , todas esas llamadas foo
se han eliminado de la pila. bar
puede ejecutarse normalmente porque la pila ya no se desborda con foo
s. (Tenga en cuenta que no creo que JLS le garantice que puede recuperarse de un desbordamiento de pila como este).
Cuando se produce , la JVM aparecerá en la posición catch, liberando la pila.
En tu ejemplo, obtiene rids de todo el foo apilado.
Porque la pila realmente no se desborda. Un mejor nombre podría ser AttemptToOverflowStack. Básicamente, lo que significa es que el último intento de ajustar el marco de la pila falla porque no hay suficiente espacio libre en la pila. La pila en realidad podría tener mucho espacio libre, pero no suficiente espacio. Entonces, cualquiera que sea la operación que haya dependido de que la llamada tenga éxito (típicamente una invocación a un método), nunca se ejecuta y todo lo que queda es que el programa se encargue de ese hecho. Lo que significa que realmente no es diferente de cualquier otra excepción. De hecho, podría atrapar la excepción en la función que realiza la llamada.