Co powoduje błąd java.lang.StackOverflowError

Odpowiedzi:

59

Sprawdź, czy nie występują rekuzyjne wywołania metod. Głównie jest to spowodowane rekurencyjnym wywołaniem metody. Prosty przykład to

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

    main.testMethod(1);
}

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

    System.out.println(i);
}

Tutaj System.out.println (i); będzie wielokrotnie umieszczany w stosie, gdy wywoływana jest metoda testMethod.

Thota Srinath
źródło
1
Myślę, że masz rację. Ale jakie jest rozwiązanie tego problemu. Ponieważ tworzymy metodę ponownego użycia, oznacza to, że tego potrzebujemy. Nie chcemy zmieniać metody. Jak więc rozwiązać ten błąd?
Ajay Sharma
1
albo wpadasz w nieskończoną pętlę!
yalematta
@yalematta, każda metoda rekurencyjna powinna mieć warunek zakończenia. Sprawdź więc, czy twoja metoda rekurencyjna jest poprawnie zaimplementowana i kończy się w zależności od pewnych warunków.
Ayaz Alifov
@AjaySharma Musimy zaprojektować nasz system tak, aby pasował do dostępnych granic pamięci, które przypisaliśmy JVM. Jeśli system zachowuje się niezręcznie z następującym błędem, musimy sprawdzić naszą bazę kodu.
Thota Srinath
23

Jednym z (opcjonalnych) argumentów JVM jest rozmiar stosu. To jest -Xss. Nie wiem, jaka jest wartość domyślna, ale jeśli całkowita ilość rzeczy na stosie przekroczy tę wartość, otrzymasz ten błąd.

Generalnie przyczyną tego jest nieskończona rekurencja, ale gdybyś to zobaczył, ślad stosu miałby więcej niż 5 ramek.

Spróbuj dodać argument -Xss (lub zwiększyć wartość jednego), aby zobaczyć, czy to zniknie.

nsayer
źródło
10

To, co faktycznie powoduje błąd java.lang.StackOverflowError, to zazwyczaj niezamierzona rekursja. Dla mnie często zdarza się, że zamierzałem wywołać super metodę dla metody przesłoniętej. Tak jak w tym przypadku:

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

Po pierwsze, warto wiedzieć, co dzieje się za kulisami, gdy wywołujemy funkcję. Argumenty i adres miejsca wywołania metody są umieszczane na stosie (patrz http://en.wikipedia.org/wiki/Stack_(abstract_data_type)#Runtime_memory_management ), aby wywoływana metoda mogła uzyskać dostęp do argumentów, a kiedy wywołana metoda jest zakończona, wykonanie może być kontynuowane po wywołaniu. Ale ponieważ nazywamy this.accelerate (przyspieszenie, maxVelocity) rekurencyjnie (rekurencja jest luźna, gdy metoda wywołuje samą siebie. Aby uzyskać więcej informacji, zobacz http://en.wikipedia.org/wiki/Recursion_(computer_science)) znajdujemy się w sytuacji znanej jako nieskończona rekurencja i wciąż gromadzimy argumenty i adres zwrotny na stosie wywołań. Ponieważ stos wywołań ma ograniczony rozmiar, w końcu zabraknie nam miejsca. Brak miejsca na stosie wywołań jest nazywany przepełnieniem. Dzieje się tak, ponieważ staramy się wykorzystać więcej miejsca na stosie niż mamy, a dane dosłownie przepełniają stos. W języku programowania Java powoduje to wyjątek środowiska wykonawczego java.lang.StackOverflow i natychmiastowe zatrzymanie programu.

Powyższy przykład jest nieco uproszczony (chociaż zdarza mi się to bardziej, niż bym chciał przyznać). To samo może się zdarzyć w bardziej okrągły sposób, co utrudnia wytropienie. Jednak ogólnie rzecz biorąc, StackOverflow jest zwykle dość łatwy do rozwiązania, gdy już się pojawi.

Teoretycznie możliwe jest również przepełnienie stosu bez rekursji, ale w praktyce wydaje się, że jest to dość rzadkie zdarzenie.

ptoinson
źródło
8

Co jest java.lang.StackOverflowError

Błąd java.lang.StackOverflowErrorjest generowany, aby wskazać, że stos aplikacji został wyczerpany z powodu głębokiej rekurencji, tj. Twój program / skrypt powtarza się zbyt głęboko.

Detale

StackOverflowErrorRozszerza VirtualMachineErrorklasę, która wskazuje, że JVM zostały lub nie zabraknie zasobów i nie może pracować dalej. Element VirtualMachineErrorrozszerzający Errorklasę służy do wskazania tych poważnych problemów, których aplikacja nie powinna przechwytywać. Metoda może nie deklarować takich błędów w swojej throwklauzuli, ponieważ te błędy są warunkami anormalnymi, których nigdy nie oczekiwano.

Przykład

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

}

Wyjście konsoli

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)

Wyjaśnienie

Gdy wywołanie funkcji jest wywoływane przez aplikację Java, na stosie wywołań alokowana jest ramka stosu . stack frameZawiera parametry metody wywołaniu jego lokalnych parametrów oraz adres zwrotny metody. Adres zwrotny oznacza punkt wykonania, z którego wykonanie programu będzie kontynuowane po powrocie wywołanej metody. Jeśli nie ma miejsca na nową ramkę stosu, StackOverflowErrorjest ona generowana przez wirtualną maszynę języka Java (JVM).

Najczęstszym przypadkiem, który może wyczerpać stos aplikacji Java, jest rekurencja. W rekurencji metoda wywołuje się podczas wykonywania. Recursionjedna z najpotężniejszych technik programowania ogólnego przeznaczenia, ale należy jej używać ostrożnie, StackOverflowErroraby jej uniknąć.

Bibliografia

DebanjanB
źródło
4

Gdy wywołanie funkcji jest wywoływane przez aplikację Java, na stosie wywołań alokowana jest ramka stosu. Ramka stosu zawiera parametry wywoływanej metody, jej parametry lokalne oraz adres zwrotny metody.

Adres zwrotny oznacza punkt wykonania, z którego będzie kontynuowane wykonanie programu po powrocie wywołanej metody. Jeśli nie ma miejsca na nową ramkę stosu, wówczas StackOverflowError jest generowany przez wirtualną maszynę Java (JVM) .

Najczęstszym przypadkiem, który może wyczerpać stos aplikacji Java, jest rekurencja.

Proszę spojrzeć

Jak rozwiązać StackOverflowError

IntelliJ Amiya
źródło
3

Rozwiązanie dla użytkowników Hibernate'a podczas analizowania danych:

Wystąpił ten błąd, ponieważ analizowałem listę obiektów zmapowanych po obu stronach @OneToManyi @ManyToOnedo json za pomocą json, co spowodowało nieskończoną pętlę.

Jeśli jesteś w tej samej sytuacji, możesz rozwiązać ten problem za pomocą adnotacji @JsonManagedReferencei @JsonBackReference.

Definicje z API:

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

    Adnotacja używana do wskazania, że ​​opisana właściwość jest częścią dwukierunkowego połączenia między polami; i że jego rolą jest łącze „nadrzędne” (lub „do przodu”). Typ wartości (klasa) właściwości musi mieć jedną zgodną właściwość z adnotacją JsonBackReference. Powiązanie jest obsługiwane w taki sposób, że właściwość z adnotacją jest obsługiwana normalnie (normalnie serializowana, bez specjalnej obsługi deserializacji); jest to pasujące odniesienie wsteczne, które wymaga specjalnej obsługi

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

    Adnotacja używana do wskazania, że ​​powiązana właściwość jest częścią dwukierunkowego połączenia między polami; i że jego rolą jest łącze „potomne” (lub „wsteczne”). Typ wartości właściwości musi być fasolą: nie może to być kolekcja, mapa, tablica ani wyliczenie. Powiązanie jest obsługiwane w taki sposób, że właściwość z adnotacją z tą adnotacją nie jest serializowana; a podczas deserializacji jego wartość jest ustawiana na wystąpienie, które ma łącze „managed” (do przodu).

Przykład:

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;

Innym rozwiązaniem jest użycie, @JsonIgnorektóre po prostu ustawi wartość null w polu.

Emerica
źródło
2

Stworzyłem program z hibernacją, w którym stworzyłem dwie klasy POJO, obie z obiektami będącymi sobą jako członkami danych. Gdy w głównej metodzie próbowałem je zapisać w bazie danych to też wyskoczył mi ten błąd.

Dzieje się tak, ponieważ obie klasy odwołują się do siebie, tworząc pętlę, która powoduje ten błąd.

Sprawdź więc, czy w twoim programie istnieją takie relacje.

Singh
źródło
1

Wyjątki związane z przepełnieniem stosu mogą wystąpić, gdy rozmiar stosu wątków nadal rośnie, aż do osiągnięcia maksymalnego limitu.

Dostosowywanie opcji rozmiarów stosu (Xss i Xmso) ...

Sugeruję, abyś zobaczył ten link: http://www-01.ibm.com/support/docview.wss?uid=swg21162896 Istnieje wiele możliwych przyczyn StackOverflowError, jak widać w linku ....

Marzieh Ghadirinia
źródło
Odpowiedzi zawierające tylko linki są generalnie niedopuszczalne; linki są zerwane, co całkowicie unieważnia odpowiedź. Podaj kontekst, kod i wyjaśnienie odpowiedzi zamiast zwykłego linku.
Jay
0

W moim przypadku mam dwa zajęcia. W drugim ćwiczeniu zapomniałem postawić super na metodzie onCreate.

super.onCreate(savedInstanceState);
Julz Etnalob
źródło
Nawet jeśli jest to możliwy sposób na podniesienie a StackOverflowError, nie sądzę, że jest to odpowiedź na pytanie. Myślę, że prawidłowa odpowiedź powinna albo wymienić inne sposoby uzyskania tego wyjątku niż użycie zbyt dużej rekurencji lub powiedzieć, że zdecydowanie nie ma innego sposobu na uzyskanie takiego wyjątku, jak tylko wyrzucenie go ręcznie.
JojOatXGME