Dê uma olhada nos dois métodos a seguir:
public static void foo() {
try {
foo();
} finally {
foo();
}
}
public static void bar() {
bar();
}
A execução bar()
resulta claramente em a StackOverflowError
, mas a execução foo()
não (o programa parece funcionar indefinidamente). Por que é que?
java
recursion
stack-overflow
try-finally
arshajii
fonte
fonte
finally
cláusula serão propagados para o próximo nível. Mas não prenda a respiração; o número de etapas executadas será de cerca de 2 à (profundidade máxima da pilha) e o lançamento de exceções também não é exatamente barato.bar()
, no entanto.Respostas:
Não dura para sempre. Cada estouro de pilha faz com que o código seja movido para o bloco final. O problema é que levará muito, muito tempo. A ordem do tempo é O (2 ^ N), em que N é a profundidade máxima da pilha.
Imagine a profundidade máxima é 5
Para trabalhar cada nível no bloco final, demore o dobro do tempo que a profundidade da pilha possa ser 10.000 ou mais. Se você puder fazer 10.000.000 de chamadas por segundo, isso levará 10 ^ 3003 segundos ou mais que a idade do universo.
fonte
-Xss
, eu tenho uma profundidade de [150 - 210], então 2 ^ n acaba sendo um número de [47 - 65] dígitos. Não vou esperar tanto, isso é o suficiente para o infinito para mim.foo
finalmente termina, resultará em umStackOverflowError
?Quando você receber uma exceção da invocação de
foo()
dentro dotry
, você chamafoo()
definally
e começar a recursão novamente. Quando isso causa outra exceção, você ligafoo()
de outro internofinally()
, e assim por diante quase ad infinitum .fonte
foo()
ser chamado finalmente após uma SOE?foo()
chamada e chamaráfoo()
nofinally
bloco da suafoo()
chamada atual .Tente executar o seguinte código:
Você descobrirá que o bloco finalmente é executado antes de lançar uma exceção no nível acima dele. (Resultado:
Isso faz sentido, como finalmente é chamado logo antes de sair do método. Isso significa, no entanto, que assim que você obtê-lo primeiro
StackOverflowError
, ele tentará lançá-lo, mas o finalmente deve ser executado primeiro, para que seja executadofoo()
novamente, o que causa um estouro de outra pilha e, como tal, é executado finalmente novamente. Isso continua acontecendo para sempre, portanto a exceção nunca é realmente impressa.No seu método de barra, no entanto, assim que a exceção ocorre, ela é lançada diretamente para o nível acima e será impressa
fonte
Em um esforço para fornecer evidências razoáveis de que isso acabará eventualmente, ofereço o seguinte código bastante sem sentido. Nota: Java NÃO é minha linguagem, por qualquer trecho da imaginação mais vívida. Eu ofereço isso apenas para apoiar a resposta de Peter, que é a resposta correta para a pergunta.
Isso tenta simular as condições do que acontece quando uma chamada NÃO pode acontecer porque introduziria um estouro de pilha. Parece-me a coisa mais difícil que as pessoas não conseguem entender, pois a invocação não acontece quando não pode acontecer.
A saída dessa pequena pilha inútil de gosma é a seguinte, e a exceção real capturada pode ser uma surpresa; Ah, e 32 tentativas (2 ^ 5), o que é totalmente esperado:
fonte
Aprenda a rastrear seu programa:
Esta é a saída que vejo:
Como você pode ver, o StackOverFlow é lançado em algumas camadas acima, portanto, você pode executar etapas de recursão adicionais até encontrar outra exceção, e assim por diante. Este é um "loop" infinito.
fonte
foo
pela segunda vez, nofinally
bloco, não está mais em umtry
. Portanto, embora ele retorne à pilha e crie mais estouros de pilha uma vez, na segunda vez, apenas retrocederá o erro produzido pela segunda chamada parafoo
, em vez de aprofundar.O programa simplesmente parece funcionar para sempre; na verdade, termina, mas leva exponencialmente mais tempo quanto mais espaço de pilha você tiver. Para provar que termina, escrevi um programa que primeiro esgota a maior parte do espaço disponível na pilha, depois chama
foo
e, finalmente, grava um rastro do que aconteceu:O código:
Você pode experimentá-lo online! (Algumas execuções podem ser feitas
foo
mais ou menos vezes que outras)fonte