Co może spowodować java.lang.StackOverflowError
? Wydruk stosu, który otrzymuję, nie jest wcale zbyt głęboki (tylko 5 metod).
Odpowiedzi:
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.
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.
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.
java.lang.StackOverflowError
Błąd java.lang.StackOverflowError
jest generowany, aby wskazać, że stos aplikacji został wyczerpany z powodu głębokiej rekurencji, tj. Twój program / skrypt powtarza się zbyt głęboko.
StackOverflowError
Rozszerza VirtualMachineError
klasę, która wskazuje, że JVM zostały lub nie zabraknie zasobów i nie może pracować dalej. Element VirtualMachineError
rozszerzający Error
klasę 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 throw
klauzuli, ponieważ te błędy są warunkami anormalnymi, których nigdy nie oczekiwano.
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);
}
}
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)
Gdy wywołanie funkcji jest wywoływane przez aplikację Java, na stosie wywołań alokowana jest ramka stosu . stack frame
Zawiera 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, StackOverflowError
jest 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. Recursion
jedna z najpotężniejszych technik programowania ogólnego przeznaczenia, ale należy jej używać ostrożnie, StackOverflowError
aby jej uniknąć.
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ć
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 @OneToMany
i @ManyToOne
do 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 @JsonManagedReference
i @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, @JsonIgnore
które po prostu ustawi wartość null w polu.
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.
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 ....
W moim przypadku mam dwa zajęcia. W drugim ćwiczeniu zapomniałem postawić super na metodzie onCreate.
super.onCreate(savedInstanceState);
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.