Was verursacht einen java.lang.StackOverflowError?

Antworten:

59

Suchen Sie nach wiederkehrenden Methodenaufrufen. Dies wird hauptsächlich verursacht, wenn eine Methode rekursiv aufgerufen wird. Ein einfaches Beispiel ist

public static void main(String... args) {
    Main main = new Main();

    main.testMethod(1);
}

public void testMethod(int i) {
    testMethod(i);

    System.out.println(i);
}

Hier das System.out.println (i); wird wiederholt zum Stapeln verschoben, wenn die testMethod aufgerufen wird.

Thota Srinath
quelle
1
Ich glaube, Du hast recht. Aber was ist die Lösung dafür? Weil wir eine Methode wiederverwenden, bedeutet dies, dass wir das brauchen. Wir wollen die Methode nicht ändern. Wie kann dieser Fehler behoben werden?
Ajay Sharma
1
oder du gerätst in eine Endlosschleife!
Yalematta
@yalematta, jede rekursive Methode sollte eine Bedingung zum Beenden haben. Überprüfen Sie daher, ob Ihre rekursive Methode ordnungsgemäß implementiert ist und je nach Bedingung abgeschlossen wird.
Ayaz Alifov
@AjaySharma Wir müssen unser System so gestalten, dass es in die verfügbaren Speichergrenzen passt, die wir JVM zugewiesen haben. Wenn sich das System mit dem folgenden Fehler unangenehm verhält, müssen wir unsere Codebasis überprüfen.
Thota Srinath
23

Eines der (optionalen) Argumente für die JVM ist die Stapelgröße. Es ist -Xss. Ich weiß nicht, was der Standardwert ist, aber wenn die Gesamtmenge des Materials auf dem Stapel diesen Wert überschreitet, wird dieser Fehler angezeigt.

Im Allgemeinen ist eine unendliche Rekursion die Ursache dafür. Wenn Sie dies jedoch sehen würden, hätte Ihre Stapelverfolgung mehr als 5 Frames.

Versuchen Sie, ein -Xss-Argument hinzuzufügen (oder den Wert von eins zu erhöhen), um festzustellen, ob dies nicht mehr funktioniert.

nsayer
quelle
10

Was tatsächlich einen java.lang.StackOverflowError verursacht, ist normalerweise eine unbeabsichtigte Rekursion. Für mich ist es oft, wenn ich beabsichtigte, eine Supermethode für die überschriebene Methode aufzurufen. Wie in diesem Fall:

public class Vehicle {
    public void accelerate(float acceleration, float maxVelocity) {
        // set the acceleration
    }
}

public class SpaceShip extends Vehicle {
    @Override
    public void accelerate(float acceleration, float maxVelocity) {
        // update the flux capacitor and call super.accelerate
        // oops meant to call super.accelerate(acceleration, maxVelocity);
        // but accidentally wrote this instead. A StackOverflow is in our future.
        this.accelerate(acceleration, maxVelocity); 
    }
}

Zunächst ist es hilfreich zu wissen, was hinter den Kulissen passiert, wenn wir eine Funktion aufrufen. Die Argumente und die Adresse, an der die Methode aufgerufen wurde, werden auf den Stapel verschoben (siehe http://en.wikipedia.org/wiki/Stack_(abstract_data_type)#Runtime_memory_management ), damit die aufgerufene Methode auf die Argumente zugreifen kann und wann Wenn die aufgerufene Methode abgeschlossen ist, kann die Ausführung nach dem Aufruf fortgesetzt werden. Da wir this.accelerate (Beschleunigung, maxVelocity) rekursiv aufrufen (Rekursion ist lose, wenn sich eine Methode selbst aufruft. Weitere Informationen finden Sie unter http://en.wikipedia.org/wiki/Recursion_(computer_science).) Wir befinden uns in einer Situation, die als unendliche Rekursion bekannt ist, und stapeln die Argumente und die Rücksprungadresse auf dem Aufrufstapel. Da der Aufrufstapel endlich ist, geht uns schließlich der Speicherplatz aus. Der Platzmangel auf dem Aufrufstapel wird als Überlauf bezeichnet. Dies liegt daran, dass wir versuchen, mehr Stapelspeicher als wir zu verwenden, und die Daten den Stapel buchstäblich überlaufen. In der Programmiersprache Java führt dies zur Laufzeitausnahme java.lang.StackOverflow und stoppt das Programm sofort.

Das obige Beispiel ist etwas vereinfacht (obwohl es mir mehr passiert, als ich zugeben möchte). Dasselbe kann auf eine rundere Art und Weise passieren, was es etwas schwieriger macht, es aufzuspüren. Im Allgemeinen ist der StackOverflow jedoch in der Regel recht einfach aufzulösen, sobald er auftritt.

Theoretisch ist es auch möglich, einen Stapelüberlauf ohne Rekursion zu haben, aber in der Praxis scheint dies ein ziemlich seltenes Ereignis zu sein.

Ptoinson
quelle
8

Was ist java.lang.StackOverflowError

Der Fehler java.lang.StackOverflowErrorwird angezeigt, um anzuzeigen, dass der Stapel der Anwendung aufgrund einer tiefen Rekursion erschöpft ist, dh Ihr Programm / Skript rekursiert zu tief.

Einzelheiten

Die StackOverflowErrorExtended- VirtualMachineErrorKlasse, die angibt, dass der JVM die Ressourcen ausgegangen sind oder ausgegangen sind und nicht weiter ausgeführt werden können. Das, VirtualMachineErrorwas die ErrorKlasse erweitert, wird verwendet, um die schwerwiegenden Probleme anzuzeigen, die eine Anwendung nicht abfangen sollte. Eine Methode darf solche Fehler nicht deklarierenthrow Klausel, da diese Fehler abnormale Bedingungen sind, von denen nie erwartet wurde, dass sie auftreten.

Ein Beispiel

Minimal, Complete, and Verifiable Example ::

package demo;

public class StackOverflowErrorExample {

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

    public static void recursivePrint(int num) {
        System.out.println("Number: " + num);

        if(num == 0)
            return;
        else
            recursivePrint(++num);
    }

}

Konsolenausgabe

Number: 1
Number: 2
.
.
.
Number: 8645
Number: 8646
Number: 8647Exception in thread "main" java.lang.StackOverflowError
    at java.io.FileOutputStream.write(Unknown Source)
    at java.io.BufferedOutputStream.flushBuffer(Unknown Source)
    at java.io.BufferedOutputStream.flush(Unknown Source)
    at java.io.PrintStream.write(Unknown Source)
    at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
    at sun.nio.cs.StreamEncoder.flushBuffer(Unknown Source)
    at java.io.OutputStreamWriter.flushBuffer(Unknown Source)
    at java.io.PrintStream.newLine(Unknown Source)
    at java.io.PrintStream.println(Unknown Source)
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:11)
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)
    .
    .
    .
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)

Erklärung

Wenn ein Funktionsaufruf von einer Java - Anwendung aufgerufen wird, ein Stapelrahmen wird auf dem zugeordneten Call - Stack . Das stack frameenthä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 fortgesetzt werden soll, nachdem die aufgerufene Methode zurückgekehrt ist. Wenn kein Platz für einen neuen Stapelrahmen vorhanden ist, wird derStackOverflowError wird dieser von der Java Virtual Machine (JVM) ausgelöst.

Der häufigste Fall, der möglicherweise den Stapel einer Java-Anwendung erschöpfen kann, ist die Rekursion. Bei der Rekursion ruft sich eine Methode während ihrer Ausführung auf. Recursioneine der leistungsstärksten Allzweck-Programmiertechniken, die jedoch mit Vorsicht angewendet werden muss, damit dieStackOverflowError damit dies vermieden wird.

Verweise

DebanjanB
quelle
4

Wenn ein Funktionsaufruf von einer Java-Anwendung aufgerufen wird, wird dem Aufrufstapel ein Stapelrahmen zugewiesen. Der Stapelrahmen 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 fortgesetzt werden soll, nachdem die aufgerufene Methode zurückgekehrt ist. Wenn kein Platz für einen neuen Stack-Frame vorhanden ist, wird der StackOverflowError von der Java Virtual Machine (JVM) ausgelöst .

Der häufigste Fall, der möglicherweise den Stapel einer Java-Anwendung erschöpfen kann, ist die Rekursion.

Guck dir das mal bitte an

So lösen Sie StackOverflowError

IntelliJ Amiya
quelle
2

Ich habe ein Programm mit Ruhezustand erstellt, in dem ich zwei POJO-Klassen erstellt habe, beide mit einem Objekt voneinander als Datenelemente. Als ich in der Hauptmethode versuchte, sie in der Datenbank zu speichern, bekam ich auch diesen Fehler.

Dies geschieht, weil beide Klassen aufeinander verweisen und somit eine Schleife erstellen, die diesen Fehler verursacht.

Überprüfen Sie also, ob in Ihrem Programm solche Beziehungen bestehen.

Singh
quelle
2

Lösung für Benutzer im Ruhezustand beim Parsen von Daten:

Ich hatte diesen Fehler, weil ich eine Liste von Objekten analysiert habe, die auf beiden Seiten @OneToManyund zugeordnet sind@ManyToOne analysierte, die json mit jackson zugeordnet waren, was eine Endlosschleife verursachte.

Wenn Sie sich in derselben Situation befinden, können Sie dies mithilfe von @JsonManagedReferenceund @JsonBackReferenceAnmerkungen lösen .

Definitionen aus der API:

  • JsonManagedReference ( https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonManagedReference.html ):

    Annotation, die angibt, dass annotierte Eigenschaften Teil einer bidirektionalen Verknüpfung zwischen Feldern sind. und dass seine Rolle "Eltern" (oder "Vorwärts") Link ist. Der Werttyp (Klasse) der Eigenschaft muss eine einzige kompatible Eigenschaft haben, die mit JsonBackReference versehen ist. Die Verknüpfung wird so behandelt, dass die mit dieser Anmerkung kommentierte Eigenschaft normal behandelt wird (normal serialisiert, keine spezielle Behandlung für die Deserialisierung). Es ist die übereinstimmende Rückreferenz, die eine besondere Behandlung erfordert

  • JsonBackReference: ( https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonBackReference.html ):

    Anmerkung, die angibt, dass die zugeordnete Eigenschaft Teil der bidirektionalen Verknüpfung zwischen Feldern ist. und dass seine Rolle "Kind" (oder "Zurück") Link ist. Der Werttyp der Eigenschaft muss eine Bean sein: Es kann sich nicht um eine Sammlung, eine Zuordnung, ein Array oder eine Aufzählung handeln. Die Verknüpfung wird so behandelt, dass die mit dieser Anmerkung versehene Eigenschaft nicht serialisiert wird. und während der Deserialisierung wird sein Wert auf eine Instanz gesetzt, die den "verwalteten" (Vorwärts-) Link hat.

Beispiel:

Owner.java:

@JsonManagedReference
@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
Set<Car> cars;

Car.java:

@JsonBackReference
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "owner_id")
private Owner owner;

Eine andere Lösung ist die Verwendung, bei @JsonIgnoreder das Feld nur auf Null gesetzt wird.

Emerica
quelle
1

Stapelüberlauf-Ausnahmen können auftreten, wenn ein Thread-Stapel weiter an Größe zunimmt, bis die maximale Grenze erreicht ist.

Anpassen der Optionen für Stapelgrößen (Xss und Xmso) ...

Ich schlage vor, Sie sehen diesen Link: http://www-01.ibm.com/support/docview.wss?uid=swg21162896 Es gibt viele mögliche Ursachen für einen StackOverflowError, wie Sie im Link sehen können ....

Marzieh Ghadirinia
quelle
Nur-Link-Antworten sind im Allgemeinen nicht akzeptabel. Links brechen, was die Antwort völlig ungültig machen würde. Bitte geben Sie einen Kontext, einen Code und eine Erklärung der Antwort anstelle eines Links an.
Jay
0

In meinem Fall habe ich zwei Aktivitäten. In der zweiten Aktivität habe ich vergessen, die onCreate-Methode super zu setzen.

super.onCreate(savedInstanceState);
Julz Etnalob
quelle
Auch wenn es ein möglicher Weg ist, eine zu erheben StackOverflowError, tue ich nicht, dass es die Frage beantwortet. Ich denke, eine richtige Antwort sollte entweder andere Möglichkeiten auflisten, um diese Ausnahme zu erhalten, als zu viel Rekursion zu verwenden, oder sagen, dass es definitiv keine andere Möglichkeit gibt, eine solche Ausnahme zu erhalten, als sie manuell auszulösen.
JojOatXGME