Ich bin überrascht, wie es möglich ist, die Ausführung auch nach einem StackOverflowError
Ereignis in Java fortzusetzen .
Ich weiß, dass dies StackOverflowError
eine Unterklasse der Klasse Error ist. Die Klasse Error wird als "eine Unterklasse von Throwable deklariert, die auf schwerwiegende Probleme hinweist, die eine vernünftige Anwendung nicht abfangen sollte".
Dies klingt eher nach einer Empfehlung als nach einer Regel, da das Abfangen eines Fehlers wie eines StackOverflowError tatsächlich zulässig ist und es der Vernünftigkeit des Programmierers überlassen ist, dies nicht zu tun. Und siehe da, ich habe diesen Code getestet und er wird normal beendet.
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");
}
}
Wie kann das sein? Ich denke, bis der StackOverflowError ausgelöst wird, sollte der Stack so voll sein, dass kein Platz mehr zum Aufrufen einer anderen Funktion vorhanden ist. Läuft der Fehlerbehandlungsblock in einem anderen Stapel oder was ist hier los?
quelle
Antworten:
Wenn der Stapel überläuft und
StackOverflowError
geworfen wird, wird der Stapel durch die übliche Ausnahmebehandlung abgewickelt. Das Abwickeln des Stapels bedeutet:... bis die Ausnahme abgefangen wird. Dies ist normal (tatsächlich notwendig) und unabhängig davon, welche Ausnahme ausgelöst wird und warum. Da Sie die Ausnahme außerhalb des ersten Aufrufs von abfangen
foo()
, wurden die Tausenden vonfoo
Stapelrahmen, die den Stapel füllten, alle abgewickelt, und der größte Teil des Stapels kann wieder verwendet werden.quelle
foo
mit einem undefinierten Zustand endet, so dass angenommen werden muss, dass jedes Objekt, das es berührt hat, gebrochen ist. Da Sie nicht wissen, in welcher Funktion der Stapelüberlauf aufgetreten ist, sondern nur, dass es sich um einen Nachkommen destry
Blocks handeln muss, der ihn abgefangen hat, ist jedes Objekt verdächtig, das mit einer von dort aus erreichbaren Methode geändert werden kann. Normalerweise lohnt es sich nicht, herauszufinden, was passiert ist, und zu versuchen, das Problem zu beheben.Error
s selbst beim Schreiben von ausnahmesicherem Code nicht vorweggenommen werden kann.Error
s. Das OP fragt nur nachStackOverflowError
und fragt nach einer bestimmten Frage zur Behandlung dieses Fehlers: Wie kann ein Methodenaufruf nicht fehlschlagen, wenn dieser Fehler abgefangen wird?Wenn der StackOverflowError ausgelöst wird, ist der Stapel voll. Wenn es jedoch abgefangen wird , wurden alle diese
foo
Aufrufe vom Stapel genommen.bar
kann normal ausgeführt werden, da der Stapel nicht mehr mitfoo
s überfüllt ist . (Beachten Sie, dass ich nicht glaube, dass das JLS garantiert, dass Sie sich von einem Stapelüberlauf wie diesem erholen können.)quelle
Wenn der StackOverFlow auftritt, wird die JVM zum Fang heruntergefahren und der Stapel freigegeben.
In Ihrem Beispiel werden alle gestapelten Foo befreit.
quelle
Weil der Stapel nicht wirklich überläuft. Ein besserer Name könnte AttemptToOverflowStack sein. Grundsätzlich bedeutet dies, dass der letzte Versuch, den Stapelrahmen anzupassen, fehlschlägt, weil nicht genügend freier Speicherplatz auf dem Stapel vorhanden ist. Der Stapel könnte tatsächlich viel Platz übrig haben, nur nicht genug Platz. Unabhängig davon, welche Operation vom erfolgreichen Aufruf des Aufrufs abhängt (normalerweise ein Methodenaufruf), wird sie nie ausgeführt, und das Programm muss sich nur noch mit dieser Tatsache befassen. Das heißt, es unterscheidet sich wirklich nicht von anderen Ausnahmen. Tatsächlich könnten Sie die Ausnahme in der Funktion abfangen, die den Aufruf ausführt.
quelle
Wie bereits beantwortet wurde , ist es möglich, Code auszuführen und insbesondere Funktionen aufzurufen, nachdem a
StackOverflowError
abgefangen wurde, da die normale Ausnahmebehandlungsprozedur der JVM den Stapel zwischen denthrow
und dencatch
Punkten abwickelt und so Stapelspeicherplatz für Sie frei macht. Und Ihr Experiment bestätigt, dass dies der Fall ist.Dies ist jedoch nicht ganz das Gleiche wie die Aussage, dass es im Allgemeinen möglich ist, sich von a zu erholen
StackOverflowError
.Ein
StackOverflowError
IS-AVirtualMachineError
, der IS-AN istError
. Wie Sie hervorheben, bietet Java einige vage Ratschläge fürError
:und Sie kommen vernünftigerweise zu dem Schluss, dass es unter bestimmten
Error
Umständen in Ordnung sein sollte , einen zu fangen . Beachten Sie, dass die Durchführung eines Experiments nicht zeigt, dass etwas im Allgemeinen sicher ist. Dies können nur die Regeln der Java-Sprache und die Spezifikationen der von Ihnen verwendeten Klassen. AVirtualMachineError
ist eine spezielle Ausnahmeklasse, da die Java-Sprachspezifikation und die Java Virtual Machine-Spezifikation Informationen zur Semantik dieser Ausnahme enthalten. Insbesondere sagt der letztere :...
Das entscheidende Problem ist, dass Sie "nicht vorhersagen können", wo oder wann ein
StackOverflowError
Wurf geworfen wird. Es gibt keine Garantie dafür, wo es nicht geworfen wird. Sie können sich nicht darauf verlassen , dass es beispielsweise beim Eintritt in eine Methode ausgelöst wird . Es könnte an einem Punkt innerhalb einer Methode geworfen werden.Diese Unvorhersehbarkeit ist möglicherweise katastrophal. Da es innerhalb einer Methode ausgelöst werden kann, kann es teilweise durch eine Folge von Operationen geworfen werden, die die Klasse als eine "atomare" Operation betrachtet, wodurch das Objekt in einem teilweise modifizierten, inkonsistenten Zustand belassen wird. Wenn sich das Objekt in einem inkonsistenten Zustand befindet, kann jeder Versuch, dieses Objekt zu verwenden, zu einem fehlerhaften Verhalten führen. In allen praktischen Fällen können Sie nicht wissen, welches Objekt sich in einem inkonsistenten Zustand befindet. Daher müssen Sie davon ausgehen, dass keine Objekte vertrauenswürdig sind. Jeder Wiederherstellungsvorgang oder Versuch, nach dem Abfangen der Ausnahme fortzufahren, kann daher ein fehlerhaftes Verhalten aufweisen. Das einzig sichere ist daher, a nicht zu fangen
StackOverflowError
, sondern um das Programm beenden zu lassen. (In der Praxis versuchen Sie möglicherweise, eine Fehlerprotokollierung durchzuführen, um die Fehlerbehebung zu unterstützen. Sie können sich jedoch nicht darauf verlassen, dass diese Protokollierung ordnungsgemäß funktioniert.) Das heißt, Sie können sich nicht zuverlässig von einem erholenStackOverflowError
.quelle