Was ist ein StackOverflowError
Was sind die Ursachen dafür, und wie gehe ich damit um?
Antworten
Zu viele Anzeigen?Der Stack hat eine Platzbegrenzung, die vom Betriebssystem abhängt. Die normale Größe beträgt 8 MB (in Ubuntu (Linux), können Sie diese Grenze mit $ ulimit -u
und kann in anderen Betriebssystemen in ähnlicher Weise überprüft werden). Jedes Programm verwendet den Stack zur Laufzeit, aber um genau zu wissen, wann er verwendet wird, müssen Sie die Assemblersprache überprüfen. In x86_64 zum Beispiel wird der Stack verwendet, um:
- Speichern der Rücksprungadresse beim Aufruf einer Prozedur
- Lokale Variablen speichern
- Speichern von Spezialregistern, um sie später wiederherzustellen
- Übergabe von Argumenten an einen Prozeduraufruf (mehr als 6)
- Sonstiges: zufällige unbenutzte Stapelbasis, kanarische Werte, Auffüllen, ... usw.
Wenn Sie x86_64 nicht kennen (Normalfall), müssen Sie nur wissen, wann die von Ihnen verwendete Hochsprache für diese Aktionen kompiliert wird. Zum Beispiel in C:
- (1) ein Funktionsaufruf
- (2) lokale Variablen in Funktionsaufrufen (einschließlich main)
- (3) lokale Variablen in Funktionsaufrufen (nicht main)
- (4) ein Funktionsaufruf
- (5) normalerweise ein Funktionsaufruf, so ist er im Allgemeinen für einen Stapelüberlauf irrelevant.
Also, in C, nur lokale Variablen und Funktionsaufrufe nutzen den Stack . Es gibt zwei (einzigartige?) Möglichkeiten, einen Stapelüberlauf zu erzeugen:
- Das Deklarieren zu großer lokaler Variablen in main oder in einer Funktion, die in ihr aufgerufen wird (
int array[10000][10000];
) - Eine sehr tiefe oder unendliche Rekursion (zu viele Funktionsaufrufe zur gleichen Zeit).
Zur Vermeidung einer StackOverflowError
können Sie:
-
prüfen, ob lokale Variablen zu groß sind (Größenordnung 1 MB), den Heap (malloc/calloc-Aufrufe) oder globale Variablen verwenden.
-
auf unendliche Rekursion prüfen, wissen Sie, was zu tun ist... korrigieren Sie es!
-
auf normale, zu tiefe Rekursion zu prüfen, ist es am einfachsten, die Implementierung so zu ändern, dass sie iterativ ist.
Beachten Sie auch, dass globale Variablen, Include-Bibliotheken usw... den Stack nicht verwenden.
Nur wenn dies nicht funktioniert, ändern Sie die Stack-Größe auf die maximale Größe des jeweiligen Betriebssystems. Bei Ubuntu zum Beispiel: ulimit -s 32768
(32 MB). (Dies war noch nie die Lösung für einen meiner Stack Overflow-Fehler, aber ich habe auch nicht viel Erfahrung).
Ich habe spezielle und/oder nicht standardmäßige Fälle in C ausgelassen (wie die Verwendung von alloc()
und ähnliches), denn wenn Sie sie verwenden, sollten Sie bereits genau wissen, was Sie tun.
Hier ein Beispiel
public static void main(String[] args) {
System.out.println(add5(1));
}
public static int add5(int a) {
return add5(a) + 5;
}
Ein StackOverflowError liegt vor, wenn Sie versuchen, etwas zu tun, das sich höchstwahrscheinlich selbst aufruft und bis ins Unendliche weitergeht (oder bis es einen StackOverflowError gibt).
add5(a)
ruft sich selbst auf, und dann wieder sich selbst, und so weiter.
Dies ist ein typischer Fall von java.lang.StackOverflowError
... Die Methode ruft sich selbst rekursiv auf, ohne dass ein Exit in doubleValue()
, floatValue()
etc.
Datei Rational.java
public class Rational extends Number implements Comparable<Rational> {
private int num;
private int denom;
public Rational(int num, int denom) {
this.num = num;
this.denom = denom;
}
public int compareTo(Rational r) {
if ((num / denom) - (r.num / r.denom) > 0) {
return +1;
} else if ((num / denom) - (r.num / r.denom) < 0) {
return -1;
}
return 0;
}
public Rational add(Rational r) {
return new Rational(num + r.num, denom + r.denom);
}
public Rational sub(Rational r) {
return new Rational(num - r.num, denom - r.denom);
}
public Rational mul(Rational r) {
return new Rational(num * r.num, denom * r.denom);
}
public Rational div(Rational r) {
return new Rational(num * r.denom, denom * r.num);
}
public int gcd(Rational r) {
int i = 1;
while (i != 0) {
i = denom % r.denom;
denom = r.denom;
r.denom = i;
}
return denom;
}
public String toString() {
String a = num + "/" + denom;
return a;
}
public double doubleValue() {
return (double) doubleValue();
}
public float floatValue() {
return (float) floatValue();
}
public int intValue() {
return (int) intValue();
}
public long longValue() {
return (long) longValue();
}
}
Datei Main.java
public class Main {
public static void main(String[] args) {
Rational a = new Rational(2, 4);
Rational b = new Rational(2, 6);
System.out.println(a + " + " + b + " = " + a.add(b));
System.out.println(a + " - " + b + " = " + a.sub(b));
System.out.println(a + " * " + b + " = " + a.mul(b));
System.out.println(a + " / " + b + " = " + a.div(b));
Rational[] arr = {new Rational(7, 1), new Rational(6, 1),
new Rational(5, 1), new Rational(4, 1),
new Rational(3, 1), new Rational(2, 1),
new Rational(1, 1), new Rational(1, 2),
new Rational(1, 3), new Rational(1, 4),
new Rational(1, 5), new Rational(1, 6),
new Rational(1, 7), new Rational(1, 8),
new Rational(1, 9), new Rational(0, 1)};
selectSort(arr);
for (int i = 0; i < arr.length - 1; ++i) {
if (arr[i].compareTo(arr[i + 1]) > 0) {
System.exit(1);
}
}
Number n = new Rational(3, 2);
System.out.println(n.doubleValue());
System.out.println(n.floatValue());
System.out.println(n.intValue());
System.out.println(n.longValue());
}
public static <T extends Comparable<? super T>> void selectSort(T[] array) {
T temp;
int mini;
for (int i = 0; i < array.length - 1; ++i) {
mini = i;
for (int j = i + 1; j < array.length; ++j) {
if (array[j].compareTo(array[mini]) < 0) {
mini = j;
}
}
if (i != mini) {
temp = array[i];
array[i] = array[mini];
array[mini] = temp;
}
}
}
}
Ergebnis
2/4 + 2/6 = 4/10
Exception in thread "main" java.lang.StackOverflowError
2/4 - 2/6 = 0/-2
at com.xetrasu.Rational.doubleValue(Rational.java:64)
2/4 * 2/6 = 4/24
at com.xetrasu.Rational.doubleValue(Rational.java:64)
2/4 / 2/6 = 12/8
at com.xetrasu.Rational.doubleValue(Rational.java:64)
at com.xetrasu.Rational.doubleValue(Rational.java:64)
at com.xetrasu.Rational.doubleValue(Rational.java:64)
at com.xetrasu.Rational.doubleValue(Rational.java:64)
at com.xetrasu.Rational.doubleValue(Rational.java:64)
Hier ist der Quellcode von StackOverflowError
in OpenJDK 7 .
- See previous answers
- Weitere Antworten anzeigen
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
).