it-swarm.com.de

Warum ist eine Wiederherstellung von einem StackOverflowError möglich?

Ich bin überrascht, wie es möglich ist, die Ausführung fortzusetzen, selbst wenn in Java eine StackOverflowError aufgetreten ist.

Ich weiß, dass StackOverflowError eine Unterklasse der Klasse Fehler ist ..__ Die Klasse Fehler wird als "eine Unterklasse von Throwable deklariert, die auf schwerwiegende Probleme hinweist, die eine vernünftige Anwendung nicht versuchen sollte, zu fangen."

Das klingt eher nach einer Empfehlung als nach einer Regel, es ist tatsächlich erlaubt, einen Fehler wie einen StackOverflowError abzufangen, und es liegt im Ermessen des Programmierers, dies nicht zu tun. Und ich habe diesen Code getestet und er wird normalerweise 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, wenn der StackOverflowError geworfen wird, sollte der Stack so voll sein, dass kein Platz zum Aufrufen einer anderen Funktion vorhanden ist. Läuft der Fehlerbehandlungsblock in einem anderen Stack oder was ist hier los?

98
user3370796

Wenn der Stapel überläuft und StackOverflowError geworfen wird, wird der Stapel bei der üblichen Ausnahmebehandlung abgewickelt. Abwickeln des Stapels bedeutet:

  • bricht die Ausführung der aktuell aktiven Funktion ab
  • löschen Sie den Stack-Frame und fahren Sie mit der aufrufenden Funktion fort
  • die Ausführung des Anrufers abbrechen
  • löschen Sie den Stack-Frame und fahren Sie mit der aufrufenden Funktion fort
  • und so weiter...

... bis die Ausnahme abgefangen wird. Dies ist normal (eigentlich notwendig) und unabhängig davon, welche Ausnahme ausgelöst wird und warum. Da Sie die Ausnahme außerhalb des ersten Aufrufs von foo() abfangen, wurden die Tausende von foo-Stack-Frames, die den Stack gefüllt haben, abgewickelt, und der größte Teil des Stack kann wieder verwendet werden.

117
user395760

Wenn der StackOverflowError geworfen wird, ist der Stapel voll. Wenn es jedoch catch ist, wurden alle diese foo-Aufrufe aus dem Stack entfernt. bar kann normal ausgeführt werden, da der Stapel nicht mehr mit foos überläuft. (Beachten Sie, dass ich nicht glaube, dass das JLS garantiert, dass Sie von einem Stapelüberlauf wie diesem wiederherstellen können.)

23
user2357112

Wenn StackOverFlow auftritt, springt die JVM auf den Haken und gibt den Stapel frei.

In Ihrem Beispiel werden alle gestapelten Foo los.

12

Weil der Stack eigentlich nicht überläuft. Ein besserer Name könnte AttemptToOverflowStack sein. Im Wesentlichen bedeutet dies, dass der letzte Versuch, den Stapelrahmen anzupassen, fehlschlägt, da nicht genügend freier Speicherplatz auf dem Stapel vorhanden ist. Der Stapel könnte tatsächlich viel Platz haben, nur nicht genügend Platz. Was auch immer die Operation davon abhängt, ob der Aufruf erfolgreich war (normalerweise ein Methodenaufruf), wird nie ausgeführt und es bleibt nur noch das Programm, das diese Tatsache behandelt. Was bedeutet, dass es sich wirklich nicht von anderen Ausnahmen unterscheidet. Tatsächlich könnten Sie die Ausnahme in der Funktion, die den Aufruf ausführt, abfangen.

8
jmoreno