Um dies zu beschreiben, müssen wir zunächst verstehen, wie . Variablen und Objekte gespeichert werden.
Lokale Variablen werden in der Stapel :
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
-
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.
-
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
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 nurInstantiationException
).