Estou surpreso em como é possível continuar a execução mesmo depois que um StackOverflowError
ocorreu em Java.
Sei que StackOverflowError
é um subalterno da classe Erro. A classe Error foi decumentada como "uma subclasse de Throwable que indica problemas sérios que um aplicativo razoável não deve tentar detectar."
Isso parece mais uma recomendação do que uma regra, subtendendo que capturar um erro como StackOverflowError é de fato permitido e depende da razoabilidade do programador não fazê-lo. E veja, eu testei esse código e ele 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");
}
}
Como isso pode ser? Acho que, no momento em que StackOverflowError é lançado, a pilha deve estar tão cheia que não há espaço para chamar outra função. O bloco de tratamento de erros está sendo executado em uma pilha diferente ou o que está acontecendo aqui?
fonte
Respostas:
Quando a pilha transborda e
StackOverflowError
é lançada, o tratamento usual de exceção a desenrola. Desenrolar a pilha significa:... até que a exceção seja detectada. Isso é normal (de fato, necessário) e independente de qual exceção é lançada e por quê. Como você detectou a exceção fora da primeira chamada a
foo()
, os milhares defoo
quadros de pilha que preencheram a pilha foram todos desenrolados e a maior parte da pilha está livre para ser usada novamente.fonte
foo
terminou com um estado indefinido, portanto, qualquer objeto que ele possa ter tocado deve ser considerado como quebrado. Como você não sabe em qual função o estouro de pilha ocorreu, apenas que deve ser descendente dotry
bloco que o capturou, qualquer objeto que possa ser modificado por qualquer método acessível a partir daí agora é suspeito. Normalmente não vale a pena descobrir o que aconteceu e tentar consertar.Error
s não pode ser antecipado mesmo ao escrever um código seguro de exceção.Error
s. O OP está pedindo apenas sobreStackOverflowError
, e está pedindo uma coisa específica sobre o tratamento desse erro: como uma chamada de método pode não falhar quando esse erro é detectado.Quando o StackOverflowError é lançado, a pilha está cheia. No entanto, quando é detectado , todas essas
foo
chamadas foram retiradas da pilha.bar
pode ser executado normalmente porque a pilha não está mais transbordando defoo
s. (Observe que não acho que o JLS garante que você possa se recuperar de um estouro de pilha como este.)fonte
Quando o StackOverFlow ocorre, a JVM vai para a captura, liberando a pilha.
No seu exemplo, ele se livra de todos os foo empilhados.
fonte
Porque a pilha não transborda. Um nome melhor pode ser AttemptToOverflowStack. Basicamente, o que significa é que a última tentativa de ajustar o frame da pilha falha porque não há espaço livre suficiente na pilha. A pilha pode realmente ter muito espaço restante, mas não espaço suficiente. Portanto, qualquer operação que dependesse do sucesso da chamada (normalmente uma invocação de método), nunca é executada e tudo o que resta é o programa lidar com esse fato. O que significa que realmente não é diferente de qualquer outra exceção. Na verdade, você pode capturar a exceção na função que está fazendo a chamada.
fonte
Como já foi respondido , é possível executar código e, em particular, chamar funções, após capturar a,
StackOverflowError
pois o procedimento normal de tratamento de exceções da JVM desenrola a pilha entrethrow
oscatch
pontos e, liberando espaço de pilha para você usar. E sua experiência confirma que é o caso.No entanto, isso não é exatamente o mesmo que dizer que, em geral, é possível recuperar de a
StackOverflowError
.A
StackOverflowError
IS-AVirtualMachineError
, que IS-ANError
. Como você observou, Java fornece alguns conselhos vagos paraError
:e você, razoavelmente, conclui que deve soar como pegar um
Error
pode estar OK em algumas circunstâncias. Observe que realizar um experimento não demonstra que algo é, em geral, seguro de fazer. Apenas as regras da linguagem Java e as especificações das classes que você usa podem fazer isso. AVirtualMachineError
é uma classe especial de exceção, porque a Java Language Specification e a Java Virtual Machine Specification fornecem informações sobre a semântica dessa exceção. Em particular, o último diz :...
O problema crucial é que você "não pode prever" onde ou quando um
StackOverflowError
será lançado. Não há garantias sobre onde ele não será lançado. Você não pode confiar que ele será lançado na entrada de um método, por exemplo. Pode ser lançado em um ponto dentro de um método.Essa imprevisibilidade é potencialmente desastrosa. Como pode ser lançado dentro de um método, ele poderia ser lançado em parte por meio de uma sequência de operações que a classe considera uma operação "atômica", deixando o objeto em um estado parcialmente modificado e inconsistente. Com o objeto em um estado inconsistente, qualquer tentativa de usar esse objeto pode resultar em comportamento incorreto. Em todos os casos práticos, você não pode saber qual objeto está em um estado inconsistente, portanto, você deve assumir que nenhum objeto é confiável. Qualquer operação de recuperação ou tentativa de continuar após a exceção ser detectada pode, portanto, ter um comportamento incorreto. A única coisa segura a fazer é, portanto, não pegar um
StackOverflowError
, mas sim para permitir que o programa seja encerrado. (Na prática, você pode tentar fazer algum registro de erros para ajudar na solução de problemas, mas não pode confiar que esse registro funcione corretamente). Ou seja, você não pode se recuperar de forma confiável de aStackOverflowError
.fonte