Schauen Sie sich die folgenden zwei Methoden an:
public static void foo() {
try {
foo();
} finally {
foo();
}
}
public static void bar() {
bar();
}
Das Ausführen führt bar()
eindeutig zu a StackOverflowError
, das Ausführen foo()
jedoch nicht (das Programm scheint nur auf unbestimmte Zeit ausgeführt zu werden). Warum ist das so?
java
recursion
stack-overflow
try-finally
arshajii
quelle
quelle
finally
Klausel ausgelöst werden, auf die nächste Ebene übertragen werden. Aber halten Sie nicht den Atem an. Die Anzahl der Schritte beträgt ungefähr 2 bis (maximale Stapeltiefe) und das Auslösen von Ausnahmen ist auch nicht gerade billig.bar()
.Antworten:
Es läuft nicht für immer. Jeder Stapelüberlauf bewirkt, dass der Code zum finally-Block verschoben wird. Das Problem ist, dass es sehr, sehr lange dauern wird. Die Zeitreihenfolge ist O (2 ^ N), wobei N die maximale Stapeltiefe ist.
Stellen Sie sich vor, die maximale Tiefe beträgt 5
Das Einarbeiten jedes Levels in den finally-Block dauert doppelt so lange und die Stapeltiefe kann 10.000 oder mehr betragen. Wenn Sie 10.000.000 Anrufe pro Sekunde tätigen können, dauert dies 10 ^ 3003 Sekunden oder länger als das Alter des Universums.
quelle
-Xss
, erhalte ich eine Tiefe von [150 - 210], sodass 2 ^ n eine [47 - 65] Ziffernzahl ist. Ich werde nicht so lange warten, das ist nahe genug an der Unendlichkeit für mich.foo
endgültig endet, wird es zu einemStackOverflowError
?Wenn Sie eine Ausnahme vom Aufruf von
foo()
inside the erhaltentry
, rufen Siefoo()
von anfinally
und beginnen erneut zu rekursieren. Wenn dies eine weitere Ausnahme verursacht, rufen Siefoo()
von einem anderen innerenfinally()
an und so weiter fast unendlich .quelle
foo()
man nach einem SOE endlich aufrufen?foo()
Aufruf zurück und rufenfoo()
imfinally
Block Ihres aktuellenfoo()
Aufrufs auf.Versuchen Sie, den folgenden Code auszuführen:
Sie werden feststellen, dass der finally-Block ausgeführt wird, bevor eine Ausnahme bis zur darüber liegenden Ebene ausgelöst wird. (Ausgabe:
Dies ist sinnvoll, da es schließlich unmittelbar vor dem Beenden der Methode aufgerufen wird. Dies bedeutet jedoch, dass, sobald Sie dies zuerst erhalten
StackOverflowError
, versucht wird, es zu werfen, aber das endgültige muss zuerst ausgeführt werden, damit esfoo()
erneut ausgeführt wird, was einen weiteren Stapelüberlauf verursacht, und als solches schließlich erneut ausgeführt wird. Dies geschieht für immer, sodass die Ausnahme nie gedruckt wird.Bei Ihrer Balkenmethode wird die Ausnahme jedoch, sobald sie auftritt, direkt auf die darüber liegende Ebene geworfen und gedruckt
quelle
Um vernünftige Beweise dafür zu liefern, dass dies irgendwann enden wird, biete ich den folgenden ziemlich bedeutungslosen Code an. Hinweis: Java ist NICHT meine Sprache. Ich biete dies nur an, um Peters Antwort zu unterstützen, die die richtige Antwort auf die Frage ist.
Dies versucht, die Bedingungen zu simulieren, die auftreten, wenn ein Aufruf NICHT stattfinden kann, da dies zu einem Stapelüberlauf führen würde. Es scheint mir das Schwierigste zu sein, was die Leute nicht verstehen, dass der Aufruf nicht stattfindet, wenn er nicht stattfinden kann .
Die Ausgabe dieses kleinen sinnlosen Haufens von Gänsehaut ist die folgende, und die tatsächlich gefangene Ausnahme kann eine Überraschung sein; Oh, und 32 Try-Calls (2 ^ 5), was durchaus zu erwarten ist:
quelle
Erfahren Sie, wie Sie Ihr Programm verfolgen:
Dies ist die Ausgabe, die ich sehe:
Wie Sie sehen können, wird der StackOverFlow auf einige Ebenen darüber geworfen, sodass Sie zusätzliche Rekursionsschritte ausführen können, bis Sie eine andere Ausnahme treffen, und so weiter. Dies ist eine Endlosschleife.
quelle
foo
beim zweiten Aufruf imfinally
Block nicht mehr in a isttry
. Während es also wieder den Stapel hinuntergeht und einmal mehr Stapelüberläufe erzeugt, wird beim zweiten Mal nur der Fehler, der durch den zweiten Aufruf von erzeugt wurdefoo
, erneut ausgelöst , anstatt ihn erneut zu vertiefen.Das Programm scheint nur für immer zu laufen; Es wird tatsächlich beendet, benötigt jedoch exponentiell mehr Zeit, je mehr Stapelspeicher Sie haben. Um zu beweisen, dass es fertig ist, habe ich ein Programm geschrieben, das zuerst den größten Teil des verfügbaren Stapelspeichers erschöpft, dann aufruft
foo
und schließlich eine Spur von dem schreibt, was passiert ist:Der Code:
Sie können es online versuchen! (Einige Läufe werden möglicherweise
foo
mehr oder weniger oft aufgerufen als andere.)quelle