Frage

Was ist ein StackOverflowError, was es bewirkt, und wie soll ich mit ihnen umgehen?

War es hilfreich?

Lösung

Parameter und lokale Variablen zugewiesen auf die Stapel (mit Referenztypen, das Objekt wohnt auf dem heap und eine Variable in den Stapel Referenzen, die auf dem Heap-Objekt) . Der Stapel lebt normalerweise in dem oberen Ende des Adressraumes und wie es verwendet wird, bis es geht in Richtung des unten des Adressraumes (dh gegen Null).

Ihr Prozess hat auch eine heap , die an der unten Ende des Prozesses lebt. Wie Sie Speicher zuweisen, kann dieser Haufen zum oberen Ende des Adressraums wachsen. Wie Sie sehen können, gibt es ein Potenzial für den Haufen zu „kollidieren“ mit dem Stapel (ein bisschen wie tektonische Platten !!!).

Die häufige Ursache für einen Stack-Überlauf ist ein schlecht rekursiven Aufruf . Typischerweise wird dies verursacht, wenn Ihre rekursive Funktionen nicht die richtige Abbruchbedingung hat, so dass er landet selbst ruft immer. Oder, wenn die Abbruchbedingung in Ordnung ist, kann es durch die Forderung zu viele rekursive Anrufe verursacht werden, bevor es zu erfüllen.

Doch mit GUI-Programmierung ist es möglich, indirekte Rekursion zu erzeugen. Zum Beispiel können Sie Ihre App Umgang mit Farbe Meldungen werden und, während sie die Verarbeitung, kann es eine Funktion aufrufen, die das System bewirkt, dass eine andere Farbe Nachricht zu senden. Hier haben Sie nicht explizit genannt sich selbst, aber die OS / VM hat es für Sie erledigt.

mit ihnen umgehen, müssen Sie Ihren Code untersuchen. Wenn Sie Funktionen haben, die sich dann rufen Sie überprüfen, ob Sie eine Abbruchbedingung haben. Wenn Sie haben, dann, dass der Check beim Aufruf der Funktion Sie mindestens eines der Argumente geändert haben, sonst wird es für die rekursiv aufgerufenen Funktion und die Abbruchbedingung keine sichtbare Veränderung sein, ist nutzlos. Auch beachten Sie, dass Ihr Stack-Speicher aus dem Speicher ausgeführt werden kann, bevor eine gültige Abbruchbedingung erreicht, so stellen Sie sicher, Ihre Methode Eingangswerte verarbeiten kann mehr rekursive Aufrufe zu erfordern.

Wenn Sie noch keine offensichtlichen rekursiven Funktionen dann bekam überprüfen, um zu sehen, wenn Sie irgendwelche Bibliotheksfunktionen sind aufrufen, die indirekt Ihre Funktion verursachen (wie die implizite Fall oben) aufgerufen werden.

Andere Tipps

Um dies zu beschreiben, lassen Sie uns zuerst verstehen, wie lokale Variablen und Objekte gespeichert werden.

Die lokale Variable gespeichert sind, in Stapel : eingeben Bild Beschreibung hier

Wenn Sie auf dem Bild sehen sollten Sie in der Lage sein zu verstehen, wie die Dinge funktionieren.

Wenn ein Funktionsaufruf von einer Java-Anwendung aufgerufen wird, wird ein Stapelrahmen auf den Call-Stack zugeordnet. Der Stapelrahmen enthält, die Parameter der aufgerufenen Methode, seine lokalen Parameter und die Rücksendeadresse des Verfahrens. Die Rücksendeadresse bezeichnet den Ausführungspunkt, von dem die Programmausführung, nachdem die aufgerufene Methode weiter kehrt soll. Wenn es keinen Platz für einen neuen Stapelrahmen ist dann wird die StackOverflowError durch die Java Virtual Machine geworfen (JVM).

Der häufigste Fall, der möglicherweise eine Java-Anwendung des Stapels erschöpfen kann, ist Rekursion. In Rekursion, ruft eine Methode selbst während seiner Ausführung. Rekursion wird als ein leistungsfähiges Programmiertechnik für allgemeine Zwecke, muss aber mit Vorsicht verwendet werden, StackOverflowError zu vermeiden.

Ein Beispiel eines StackOverflowError werfen ist wie folgt:

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, genannt recursivePrint, die eine ganze Zahl druckt und dann nennt mich, mit der nächstfolgenden ganzen Zahl als Argument. Die Rekursion endet, bis wir in 0 als Parameter übergeben. Doch in unserem Beispiel haben wir in dem Parameter von 1 und seinen zunehmenden Anhängern geführt, damit die Rekursion wird niemals enden.

Eine Probe Ausführung, unter Verwendung des -Xss1M Flag, das die Größe des Thread-Stack angibt 1MB zu entsprechen, ist unten gezeigt:

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 Ausgangskonfiguration des JVM, können die Ergebnisse abweichen, aber schließlich wird die StackOverflowError geworfen werden. Dieses Beispiel ist ein sehr gutes Beispiel dafür, wie Rekursion kann zu Problemen führen, wenn sie nicht mit Vorsicht umgesetzt werden.

Wie mit dem Stackoverflow

beschäftigen
  1. Die einfachste Lösung ist, um sorgfältig den Stack-Trace zu untersuchen und das sich wiederholende Muster von Zeilennummern erfassen. Diese Zeilennummern zeigen Sie den Code rekursiv aufgerufen wird. Sobald erkennen Sie diese Linien, müssen Sie Ihren Code und verstehen, warum die sorgfältig prüfen Rekursion endet nie.

  2. Wenn Sie, dass die Rekursion prüfen     korrekt implementiert ist, können Sie die Größe des Stapels erhöhen,     Um eine größere Anzahl von Anrufungen zu ermöglichen. Je nach Java     Virtual Machine (JVM) installiert ist, die Standard-Thread-Stapelgröße kann     entweder gleich 512 KB oder 1 MB . Sie können den Thread-Stack erhöhen     die Größe -Xss Flag verwendet. Dieses Flag kann entweder über die angegeben werden     Projektkonfiguration oder über die Kommandozeile. Das Format der     -Xss Argument ist:     -Xss<size>[g|G|m|M|k|K]

Wenn Sie eine Funktion wie:

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

Dann foo () wird sich halten Aufruf, immer tiefer und tiefer, und wenn der Raum im Auge zu behalten verwendet, welche Funktionen sind Sie in voll ist, erhalten Sie den Stack-Überlauf-Fehler.

Stack-Überlauf bedeutet genau das: ein Stapel überläuft. Normalerweise gibt es einen einen Stapel in dem Programm, die lokale-scope Variablen und Adressen enthält, in dem, wenn die Ausführung einer Routine endet zurückzukehren. Dieser Stapel neigt dazu, einen festen Speicherbereich irgendwo im Speicher zu sein, daher begrenzt, es ist, wie viel es Werte enthalten.

Wenn der Stapel leer ist, können Sie nicht Pop, wenn Sie das tun werden Sie Unterlauf Fehler stapeln lassen.

Wenn der Stapel voll ist, können Sie nicht drücken, wenn Sie das tun werden Sie Stack-Überlauf-Fehler.

So Stack-Überlauf, in dem Sie zu viel in den Stapel zuordnen. Zum Beispiel in der genannten Rekursion.

optimieren Einige Implementierungen einige Formen der Rekursion aus. Endrekursion insbesondere. Tail rekursive Routinen sind Form von Routinen, wo der rekursive Aufruf erscheint als eine letzte Sache, was die Routine tut. Eine solche Routine Anruf wird in einem Sprung einfach reduziert werden.

Einige Implementierungen so weit gehen, ihre eigenen Stacks für Rekursion implementieren, daher erlauben sie die Rekursion fortzusetzen, bis das System über genügend Arbeitsspeicher ausgeführt.

Die einfachste Sache, die Sie versuchen könnten, wären Ihre Stackgröße zu erhöhen, wenn Sie können. Wenn Sie allerdings nicht das tun können, würde die zweitbeste Sache zu suchen, ob es etwas gibt, die eindeutig den Stapelüberlauf verursacht. Versuchen Sie es mit etwas Druck vor und nach dem Aufruf in der Routine. Dies hilft Ihnen, die fehlerhafte Routine zu erfahren.

Ein Stapelüberlauf in der Regel durch Verschachtelung Funktion aufgerufen wird, ruft zu tief (besonders einfach, wenn Rekursion, dh eine Funktion, die sich selbst aufruft) oder eine große Menge an Speicher auf dem Stack Zuteilung, wo der Heap mit angemessener wäre.

Wie Sie sagen, müssen Sie einige Code zu erhalten. : -)

Ein Stapelüberlauffehler tritt in der Regel, wenn Ihre Funktion Nest zu tief nennt. Siehe die Stack-Überlauf Code Golf Thread für einige Beispiele dafür, wie dies geschieht (wenn auch in der Fall dieser Frage verursachen die Antworten absichtlich Stapelüberlauf).

Die häufigste Ursache von Stapelüberlauf ist zu tiefe oder unendliche Rekursion . Wenn dies Ihr Problem, dieses Tutorial zu Java Rekursion könnte helfen, das Problem zu verstehen.

StackOverflowError ist auf dem Stapel als OutOfMemoryError auf dem Heap ist.

Unbounded rekursive Aufrufe führen in Stapelspeicher wird verbraucht.

Das folgende Beispiel erzeugt StackOverflowError:

class  StackOverflowDemo
{
    public static void unboundedRecursiveCall() {
     unboundedRecursiveCall();
    }

    public static void main(String[] args) 
    {
        unboundedRecursiveCall();
    }
}

StackOverflowError ist vermeidbar, wenn die rekursiven Aufrufe Gesamtsumme unvollständiger Anrufe in-memory (in Bytes) überschreitet die Stapelgröße (in Bytes).

begrenzt sind zu verhindern,

Hier ist ein Beispiel eines rekursiven Algorithmus für eine einfach verkettete Liste rückgängig zu machen. Auf einem Laptop mit der folgenden Spezifikation (4G-Speicher, Intel Core i5 2,3 GHz CPU, 64-Bit-Windows 7), wird diese Funktion in Stackoverflow-Fehler für eine verknüpfte Liste der Größe der Nähe von 10.000 laufen.

Mein Punkt ist, dass wir Rekursion umsichtig verwenden sollten, wobei stets berücksichtigt die Skala des Systems. Oft kann Rekursion iteratives Programm umgewandelt werden, die besser skaliert. (Eine iterative Version des gleichen Algorithmus am unteren Rand der Seite gegeben wird, kehrt er eine einfach verkettete Liste der Größe 1 Million in 9 Millisekunden.)

    private static LinkedListNode doReverseRecursively(LinkedListNode x, LinkedListNode first){

    LinkedListNode second = first.next;

    first.next = x;

    if(second != null){
        return doReverseRecursively(first, second);
    }else{
        return first;
    }
}

public static LinkedListNode reverseRecursively(LinkedListNode head){
    return doReverseRecursively(null, head);
}

Iterative Version des gleichen Algorithmus:

    public static LinkedListNode reverseIteratively(LinkedListNode head){
    return doReverseIteratively(null, head);
}   

private static LinkedListNode doReverseIteratively(LinkedListNode x, LinkedListNode first) {

    while (first != null) {
        LinkedListNode second = first.next;
        first.next = x;
        x = first;

        if (second == null) {
            break;
        } else {
            first = second;
        }
    }
    return first;
}


public static LinkedListNode reverseIteratively(LinkedListNode head){
    return doReverseIteratively(null, head);
}

Ein StackOverflowError ist ein Laufzeitfehler in Java.

Es wird ausgelöst, wenn die Menge des Call-Stack-Speicher von JVM zugewiesen überschritten wird.

Ein häufiger Fall eines StackOverflowError geworfen wird, ist, wenn Call-Stack durch übermäßige tief oder unendliche Rekursion überschreitet.

Beispiel:

public class Factorial {
    public static int factorial(int n){
        if(n == 1){
            return 1;
        }
        else{
            return n * factorial(n-1);
        }
    }

    public static void main(String[] args){
        System.out.println("Main method started");
        int result = Factorial.factorial(-1);
        System.out.println("Factorial ==>"+result);
        System.out.println("Main method ended");
    }
}

Stack-Trace:

Main method started
Exception in thread "main" java.lang.StackOverflowError
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)

Im obigen Fall kann es durch tun programmatische Änderungen vermieden werden. Aber wenn die Programmlogik korrekt ist und es immer noch auftritt, dann stapeln Sie Größe erhöht werden muss.

Hier ist ein Beispiel

public static void main(String[] args) {
    System.out.println(add5(1));
}

public static int add5(int a) {
    return add5(a) + 5;
}

Ein Stackoverflow im Grunde ist, wenn Sie versuchen, etwas zu tun, die am ehesten selbst nennt, und geht für die Unendlichkeit auf (oder bis es gibt eine Stackoverflow).

add5(a) rufen selbst, und sich dann wieder aufrufen, und so weiter.

Dies ist ein typischer Fall von java.lang.StackOverflowError ... Die Methode rekursiv ruft selbst ohne Ausgang in doubleValue(), floatValue(), etc.

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();
        }
    }

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

Der Begriff „Überlauf (Überlauf) stapelt“ wird oft verwendet, aber eine falsche Bezeichnung; Angriffe nicht überlaufen den Stapel sondern Puffer auf den Stapel.

- von Vorlesungsfolien von Prof. Dr. Dieter Gollmann

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top