Jetez un œil aux deux méthodes suivantes:
public static void foo() {
try {
foo();
} finally {
foo();
}
}
public static void bar() {
bar();
}
L'exécution bar()
entraîne clairement un StackOverflowError
, mais foo()
pas (le programme semble simplement fonctionner indéfiniment). Pourquoi donc?
java
recursion
stack-overflow
try-finally
arshajii
la source
la source
finally
clause se propageront au niveau supérieur. Mais ne retenez pas votre souffle; le nombre d'étapes prises sera d'environ 2 à la (profondeur de pile maximale) et le lancement d'exceptions n'est pas exactement bon marché non plus.bar()
, cependant.Réponses:
Cela ne fonctionne pas éternellement. Chaque débordement de pile entraîne le déplacement du code vers le bloc finalement. Le problème est que cela prendra très, très longtemps. L'ordre de temps est O (2 ^ N) où N est la profondeur de pile maximale.
Imaginez que la profondeur maximale est de 5
Pour travailler chaque niveau dans le bloc enfin, prenez deux fois plus longtemps et la profondeur de la pile peut être de 10 000 ou plus. Si vous pouvez faire 10 000 000 d'appels par seconde, cela prendra 10 ^ 3003 secondes ou plus que l'âge de l'univers.
la source
-Xss
, j'obtiens une profondeur de [150 - 210], donc 2 ^ n finit par être un nombre de [47 - 65] chiffres. Ne pas attendre aussi longtemps, c'est assez proche de l'infini pour moi.foo
se termine finalement, cela se traduira par unStackOverflowError
?Lorsque vous obtenez une exception de l'invocation de
foo()
inside thetry
, vous appelezfoo()
definally
et recommencez à récurser. Lorsque cela provoque une autre exception, vous appelezfoo()
d'un autre intérieurfinally()
, et ainsi de suite presque à l' infini .la source
foo()
on appeler enfin après un SOE?foo()
invocation et invoquerezfoo()
dans lefinally
bloc de votrefoo()
invocation actuelle .Essayez d'exécuter le code suivant:
Vous constaterez que le bloc finally s'exécute avant de lancer une exception jusqu'au niveau supérieur. (Production:
Cela a du sens, car finalement est appelé juste avant de quitter la méthode. Cela signifie, cependant, qu'une fois que vous l'obtiendrez en premier
StackOverflowError
, il essaiera de le lancer, mais que finalement doit s'exécuter en premier, donc il s'exécute àfoo()
nouveau, ce qui entraîne un autre débordement de pile, et en tant que tel s'exécute enfin à nouveau. Cela se produit pour toujours, donc l'exception n'est jamais réellement imprimée.Dans votre méthode de barre cependant, dès que l'exception se produit, elle est simplement lancée directement au niveau supérieur et sera imprimée
la source
Dans le but de fournir des preuves raisonnables que ce sera finalement résilié, j'offre le code suivant, plutôt insignifiant. Remarque: Java n'est PAS mon langage, à tout point de vue de l'imagination la plus vive. Je profère de cela uniquement pour appuyer la réponse de Peter, qui est la bonne réponse à la question.
Cela tente de simuler les conditions de ce qui se passe lorsqu'une invocation ne peut PAS se produire car elle introduirait un débordement de pile. Il me semble que la chose la plus difficile que les gens ne parviennent pas à saisir est que l'invocation ne se produit pas lorsqu'elle ne peut pas se produire.
La sortie de ce petit tas inutile de goo est la suivante, et l'exception réelle capturée peut surprendre; Oh, et 32 try-calls (2 ^ 5), ce qui est tout à fait attendu:
la source
Apprenez à retracer votre programme:
Voici la sortie que je vois:
Comme vous pouvez le voir, StackOverFlow est projeté sur certaines couches ci-dessus, vous pouvez donc effectuer des étapes de récursivité supplémentaires jusqu'à ce que vous rencontriez une autre exception, etc. Il s'agit d'une "boucle" infinie.
la source
foo
la deuxième fois, dans lefinally
bloc, il n'est plus dans atry
. Ainsi, alors qu'il redescendra la pile et créera plus de débordements de pile une fois, la deuxième fois, il renverra simplement l'erreur produite par le deuxième appel àfoo
, au lieu de réapprofondir.Le programme semble simplement fonctionner pour toujours; il se termine en fait, mais cela prend exponentiellement plus de temps lorsque vous disposez de plus d'espace de pile. Pour prouver qu'il se termine, j'ai écrit un programme qui épuise d'abord la majeure partie de l'espace de pile disponible, puis appelle
foo
et écrit enfin une trace de ce qui s'est passé:Le code:
Vous pouvez l' essayer en ligne! (Certaines courses peuvent appeler
foo
plus ou moins de fois que d'autres)la source