530 Stimmen

Was ist ein StackOverflowFehler?

Was ist ein StackOverflowError Was sind die Ursachen dafür, und wie gehe ich damit um?

0 Stimmen

Die Stapelgröße in Java ist klein. Und manchmal, z.B. bei vielen rekursiven Aufrufen, steht man vor diesem Problem. Sie können Ihren Code durch Schleifen umgestalten. Ein allgemeines Entwurfsmuster dafür finden Sie auf dieser Seite: jndanial.com/73

0 Stimmen

Eine nicht offensichtliche Möglichkeit, dies zu erreichen: Fügen Sie die Zeile new Object() {{getClass().newInstance();}}; zu einem statischen Kontext (z. B. main Methode). Funktioniert nicht aus dem Instanzkontext heraus (wirft nur InstantiationException ).

461voto

Sean Punkte 58522

Parameter und lokale Variablen werden auf der Registerkarte Stapel (bei Referenztypen lebt das Objekt auf der Haufen und eine Variable auf dem Stack verweist auf dieses Objekt auf dem Heap). Der Stack befindet sich normalerweise auf dem obere Ende Ihres Adressraums, und wenn er aufgebraucht ist, geht er zum unten des Adressraums (d. h. gegen Null).

Ihr Prozess hat auch eine Haufen , die in der unten Ende Ihres Prozesses. Wenn Sie Speicher zuweisen, kann dieser Heap bis an das obere Ende Ihres Adressraums wachsen. Wie Sie sehen können, besteht die Möglichkeit, dass der Heap "kollidieren" mit dem Stapel (ein bisschen wie tektonische Platten!!!).

Die häufigste Ursache für einen Stapelüberlauf ist eine schlechter rekursiver Aufruf . Normalerweise wird dies verursacht, wenn Ihre rekursiven Funktionen nicht die richtige Abbruchbedingung haben, so dass sie sich selbst immer wieder aufrufen. Oder wenn die Abbruchbedingung in Ordnung ist, kann sie dadurch verursacht werden, dass zu viele rekursive Aufrufe erforderlich sind, bevor sie erfüllt wird.

Mit der GUI-Programmierung ist es jedoch möglich, die indirekte Rekursion . Ihre Anwendung kann zum Beispiel Paint-Nachrichten verarbeiten und während der Verarbeitung eine Funktion aufrufen, die das System veranlasst, eine weitere Paint-Nachricht zu senden. Diese Funktion haben Sie nicht explizit selbst aufgerufen, sondern das Betriebssystem/VM hat dies für Sie getan.

Um mit ihnen fertig zu werden, müssen Sie Ihren Code untersuchen. Wenn Sie Funktionen haben, die sich selbst aufrufen, dann überprüfen Sie, ob Sie eine Abbruchbedingung haben. Wenn ja, dann überprüfen Sie, ob Sie beim Aufruf der Funktion mindestens eines der Argumente geändert haben, sonst gibt es keine sichtbare Änderung für die rekursiv aufgerufene Funktion und die Abbruchbedingung ist nutzlos. Denken Sie auch daran, dass der Speicherplatz auf dem Stack zu Ende gehen kann, bevor eine gültige Abbruchbedingung erreicht ist. Stellen Sie daher sicher, dass Ihre Methode Eingabewerte verarbeiten kann, die weitere rekursive Aufrufe erfordern.

Wenn Sie keine offensichtlichen rekursiven Funktionen haben, überprüfen Sie, ob Sie irgendwelche Bibliotheksfunktionen aufrufen, die indirekt bewirkt, dass Ihre Funktion aufgerufen wird (wie im obigen impliziten Fall).

3 Stimmen

Original-Poster: Hey, das ist großartig. Also ist Rekursion immer für Stack Overflows verantwortlich? Oder können auch andere Dinge dafür verantwortlich sein? Leider verwende ich eine Bibliothek... aber nicht eine, die ich verstehe.

5 Stimmen

Ha ha ha, also hier ist es: while (points < 100) {addMouseListeners(); moveball(); checkforcollision(); pause(speed);} Wow, wie blöd ich mich fühle, weil ich nicht gemerkt habe, dass ich am Ende einen ganzen Stapel von Maus-Listenern haben würde... Danke Leute!

9 Stimmen

Nein, Stack Overflows können auch dadurch entstehen, dass Variablen zu groß sind, um sie auf dem Stack zu allozieren, wenn Sie den Wikipedia-Artikel dazu nachschlagen unter de.wikipedia.org/wiki/Stack_overflow .

138voto

Varun Punkte 6161

Um dies zu beschreiben, müssen wir zunächst verstehen, wie . Variablen und Objekte gespeichert werden.

Lokale Variablen werden in der Stapel :

Enter image description here

Wenn Sie sich das Bild ansehen, sollten Sie verstehen können, wie die Dinge funktionieren.

Wenn eine Java-Anwendung eine Funktion aufruft, wird ein Stapelrahmen auf dem Aufrufstapel zugewiesen. Der Stack-Frame enthält die Parameter der aufgerufenen Methode, ihre lokalen Parameter und die Rücksprungadresse der Methode. Die Rücksprungadresse gibt den Ausführungspunkt an, von dem aus die Programmausführung nach der Rückkehr der aufgerufenen Methode fortgesetzt werden soll. Wenn kein Platz für einen neuen Stapelrahmen vorhanden ist, wird der StackOverflowError wird von der Java Virtual Machine (JVM) ausgelöst.

Der häufigste Fall, in dem sich der Stack einer Java-Anwendung erschöpfen kann, ist die Rekursion. Bei der Rekursion ruft sich eine Methode während ihrer Ausführung selbst auf. Die Rekursion gilt als leistungsfähige Allzweck-Programmiertechnik, muss aber mit Vorsicht eingesetzt werden, um zu vermeiden, dass StackOverflowError .

Ein Beispiel für das Werfen einer StackOverflowError ist unten dargestellt:

StackOverflowErrorExample.java:

public class StackOverflowErrorExample {

    public static void recursivePrint(int num) {
        System.out.println("Number: " + num);
        if (num == 0)
            return;
        else
            recursivePrint(++num);
        }

    public static void main(String[] args) {
        StackOverflowErrorExample.recursivePrint(1);
    }
}

In diesem Beispiel definieren wir eine rekursive Methode, die recursivePrint die eine ganze Zahl ausgibt und sich dann selbst aufruft, mit der nächstfolgenden ganzen Zahl als Argument. Die Rekursion endet, bis wir in 0 als Parameter. In unserem Beispiel haben wir jedoch den Parameter von 1 und seine aufsteigenden Nachfolger übergeben, so dass die Rekursion nie beendet wird.

Ein Ausführungsbeispiel, das die -Xss1M Flag, das die Größe des Thread-Stacks auf 1 MB festlegt, ist unten dargestellt:

Number: 1
Number: 2
Number: 3
...
Number: 6262
Number: 6263
Number: 6264
Number: 6265
Number: 6266
Exception in thread "main" java.lang.StackOverflowError
        at java.io.PrintStream.write(PrintStream.java:480)
        at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
        at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
        at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
        at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
        at java.io.PrintStream.write(PrintStream.java:527)
        at java.io.PrintStream.print(PrintStream.java:669)
        at java.io.PrintStream.println(PrintStream.java:806)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        ...

Je nach der anfänglichen Konfiguration der JVM können die Ergebnisse unterschiedlich sein, aber letztendlich wird die StackOverflowError geworfen werden. Dieses Beispiel ist ein sehr gutes Beispiel dafür, wie eine Rekursion zu Problemen führen kann, wenn sie nicht mit Bedacht implementiert wird.

Wie man mit dem StackOverflowError umgeht

  1. Die einfachste Lösung besteht darin, den Stack-Trace sorgfältig zu untersuchen und das sich wiederholende Muster von Zeilennummern zu erkennen. Diese Zeilennummern zeigen den Code an, der rekursiv aufgerufen wird. Sobald Sie diese Zeilen entdeckt haben, müssen Sie Ihren Code genau untersuchen und verstehen, warum die Rekursion nie beendet wird.

  2. Wenn Sie überprüft haben, dass die Rekursion korrekt implementiert ist, können Sie die Größe des Stacks erhöhen, um um eine größere Anzahl von Aufrufen zu ermöglichen. Abhängig von der Java Virtual Machine (JVM) kann die Standardgröße des Thread-Stacks entweder gleich 512 KB, oder 1 MB . Sie können die Größe des Thread-Stapels vergrößern, indem Sie die -Xss Flagge. Dieses Flag kann entweder über die Projektkonfiguration oder über die Befehlszeile angegeben werden. Das Format der -Xss Argument ist: -Xss<size>[g|G|m|M|k|K]

0 Stimmen

In einigen Java-Versionen scheint es unter Windows einen Fehler zu geben, bei dem das Argument -Xss nur bei neuen Threads wirksam wird

70voto

Khoth Punkte 12918

Wenn Sie eine Funktion haben wie:

int foo()
{
    // more stuff
    foo();
}

Dann ruft sich foo() immer wieder selbst auf und wird immer tiefer und tiefer, und wenn der Speicherplatz, der verwendet wird, um zu verfolgen, in welchen Funktionen Sie sich befinden, voll ist, erhalten Sie den Stack Overflow-Fehler.

14 Stimmen

Falsch. Ihre Funktion ist tail-recursive. Die meisten kompilierten Sprachen haben Optimierungen für tail-recursion. Das bedeutet, dass die Rekursion zu einer einfachen Schleife reduziert wird und Sie mit diesem Code auf einigen Systemen nie einen Stapelüberlauf erreichen werden.

0 Stimmen

Cheery, welche nicht-funktionalen Sprachen unterstützen Tail-Rekursion?

0 Stimmen

@banister und einige Implementierungen von javascript

27voto

Cheery Punkte 22879

Stapelüberlauf bedeutet genau das: ein Stapel läuft über. Normalerweise gibt es im Programm einen Stack, der Variablen mit lokalem Geltungsbereich und Adressen enthält, an die zurückzukehren ist, wenn die Ausführung einer Routine endet. Dieser Stack ist in der Regel ein fester Speicherbereich irgendwo im Speicher und kann daher nur begrenzt Werte enthalten.

Wenn der Stack leer ist, kann nicht gepoppt werden, und wenn doch, wird ein Stack-Underflow-Fehler angezeigt.

Wenn der Stack voll ist, kann man nicht pushen, und wenn man es doch tut, bekommt man einen Stack Overflow Error.

Ein Stapelüberlauf tritt dann auf, wenn zu viel auf dem Stapel gespeichert wird. Zum Beispiel in der erwähnten Rekursion.

Einige Implementierungen optimieren einige Formen von Rekursionen. Insbesondere die Tail-Rekursion. Tail-Rekursionsroutinen sind eine Form von Routinen, bei denen der rekursive Aufruf als letzter Punkt der Routine erscheint. Solche Routinenaufrufe werden einfach auf einen Sprung reduziert.

Einige Implementierungen gehen so weit, dass sie eigene Stapel für die Rekursion implementieren, so dass die Rekursion fortgesetzt werden kann, bis dem System der Speicher ausgeht.

Das Einfachste, was Sie versuchen könnten, wäre, Ihre Stapelgröße zu erhöhen, wenn Sie können. Wenn das nicht möglich ist, wäre die zweitbeste Lösung, zu prüfen, ob es etwas gibt, das den Stack-Überlauf verursacht. Versuchen Sie es, indem Sie vor und nach dem Aufruf der Routine etwas ausdrucken. Dies hilft Ihnen, die fehlerhafte Routine herauszufinden.

5 Stimmen

Gibt es so etwas wie einen Stapel Unterlauf ?

6 Stimmen

Ein Stack-Underflow ist in Assembler möglich (es wird mehr gepoppt als gepusht), aber in kompilierten Sprachen ist das fast unmöglich. Ich bin mir nicht sicher, vielleicht können Sie eine Implementierung von C's alloca() finden, die negative Größen "unterstützt".

2 Stimmen

Stapelüberlauf bedeutet genau das: ein Stapel läuft über. Normalerweise gibt es einen Stack im Programm, der Variablen mit lokalem Gültigkeitsbereich enthält -> Nein, jeder Thread hat seinen eigenen Stack, der Stack Frames für jeden Methodenaufruf enthält, der lokale Variablen enthält

11voto

Greg Punkte 306033

Ein Stapelüberlauf wird in der Regel dadurch ausgelöst, dass Funktionsaufrufe zu tief verschachtelt werden (besonders leicht bei der Verwendung von Rekursion, d. h. einer Funktion, die sich selbst aufruft) oder dass eine große Menge an Speicher auf dem Stapel zugewiesen wird, wo die Verwendung des Heaps sinnvoller wäre.

1 Stimmen

Ups, ich habe das Java-Tag nicht gesehen

0 Stimmen

Auch von der ursprünglichen Poster hier: Verschachtelung Funktionen zu tief in was? In andere Funktionen? Und: Wie weist man dem Stack oder Heap Speicher zu (da ich eines dieser Dinge eindeutig getan habe, ohne es zu wissen).

0 Stimmen

@Ziggy: Ja, wenn eine Funktion eine andere Funktion aufruft, die wiederum eine andere Funktion aufruft, und so weiter, wird Ihr Programm nach vielen Stufen einen Stapelüberlauf haben. [weiter]

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X