Me sorprende cómo es posible continuar la ejecución incluso después de que se StackOverflowError
haya producido un error en Java.
Sé que StackOverflowError
es una subclase de la clase Error. La clase Error está documentada como "una subclase de Throwable que indica problemas graves que una aplicación razonable no debería intentar detectar".
Esto suena más a una recomendación que a una regla, subtendiendo que detectar un error como un StackOverflowError está permitido 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 cuando se lance StackOverflowError, la pila debería estar tan llena que no haya espacio para llamar a otra función. ¿El bloque de manejo de errores se está ejecutando en una pila diferente o qué está sucediendo aquí?
fuente
Respuestas:
Cuando la pila se desborda y
StackOverflowError
se lanza, el manejo habitual de excepciones desenrolla la pila. Desenrollar la pila significa:... hasta que se capture la excepción. Esto es normal (de hecho, necesario) e independiente de qué excepción se lanza y por qué. Dado que detecta la excepción fuera de la primera llamada a
foo()
, los miles defoo
marcos de pila que llenaban la pila se han desenrollado y la mayor parte de la pila está libre para volver a usarse.fuente
foo
terminó con un estado indefinido, por lo que cualquier objeto que pueda haber tocado debe asumirse que está roto. Como no sabe en qué función se produjo el desbordamiento de la pila, solo que debe ser un descendiente deltry
bloque que lo atrapó, cualquier objeto que pueda ser modificado por cualquier método accesible desde allí ahora es sospechoso. Por lo general, no vale la pena averiguar qué sucedió e intentar solucionarlo.Error
no se puede anticipar s incluso cuando se escribe código seguro de excepción.Error
s. El OP solo pregunta sobreStackOverflowError
el manejo de este error , y pregunta algo específico : cómo puede no fallar una llamada a un método cuando se detecta este error.Cuando se lanza StackOverflowError, la pila está llena. Sin embargo, cuando se detecta , todas esas
foo
llamadas se han eliminado de la pila.bar
se puede ejecutar normalmente porque la pila ya no se desborda confoo
s. (Tenga en cuenta que no creo que JLS garantice que pueda recuperarse de un desbordamiento de pila como este).fuente
Cuando se produce StackOverFlow, la JVM aparecerá en el punto de captura, liberando la pila.
En su ejemplo, se deshace de todos los tontos apilados.
fuente
Porque la pila en realidad 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 es erróneo porque no queda suficiente espacio libre en la pila. La pila podría tener mucho espacio restante, pero no suficiente. Entonces, cualquier operación que hubiera dependido de que la llamada tuviera éxito (típicamente una invocación de método), nunca se ejecuta y todo lo que queda es que el programa se ocupe de ese hecho. Lo que significa que realmente no es diferente de cualquier otra excepción. De hecho, podría detectar la excepción en la función que realiza la llamada.
fuente
Como ya se ha respondido , es posible ejecutar código, y en particular llamar funciones, después de capturar un
StackOverflowError
porque el procedimiento normal de manejo de excepciones de la JVM desenrolla la pila entrethrow
loscatch
puntos y, liberando espacio de pila para su uso. Y su experimento confirma que ese es el caso.Sin embargo, eso no es lo mismo que decir que, en general, es posible recuperarse de un
StackOverflowError
.A
StackOverflowError
IS-AVirtualMachineError
, que ES-ANError
. Como señala, Java proporciona algunos consejos vagos paraError
:y usted, razonablemente, llega a la conclusión de que debería
Error
parecer que atrapar y podría estar bien en algunas circunstancias. Tenga en cuenta que realizar un experimento no demuestra que, en general, algo sea seguro. Solo las reglas del lenguaje Java y las especificaciones de las clases que usa pueden hacer eso. AVirtualMachineError
es una clase especial de excepción, porque la Especificación del lenguaje Java y la Especificación de la máquina virtual Java proporcionan información sobre la semántica de esta excepción. En particular, este último dice :...
El problema crucial es que "no se puede predecir" dónde o cuándo se
StackOverflowError
lanzará un . No hay garantías sobre dónde no se lanzará. No puede confiar en que se lance al ingresar a un método, por ejemplo. Podría lanzarse en un punto dentro de un método.Esta imprevisibilidad es potencialmente desastrosa. Como se puede lanzar dentro de un método, podría lanzarse en parte a través de una secuencia de operaciones que la clase considera una operación "atómica", dejando el objeto en un estado parcialmente modificado, inconsistente. Con el objeto en un estado inconsistente, cualquier intento de usar ese objeto podría resultar en un comportamiento erróneo. En todos los casos prácticos, no puede saber qué objeto se encuentra en un estado inconsistente, por lo que debe asumir que ningún objeto es confiable. Por lo tanto, cualquier operación de recuperación o intento de continuar después de que se detecta la excepción podría tener un comportamiento erróneo. Por lo tanto, lo único seguro que puede hacer es no atrapar
StackOverflowError
, sino más bien para permitir que el programa termine. (En la práctica, puede intentar hacer un registro de errores para ayudar a solucionar problemas, pero no puede confiar en que el registro funcione correctamente). Es decir, no puede recuperarse de forma fiable de unStackOverflowError
.fuente