Co tak naprawdę powoduje błąd przepełnienia stosu? [duplikat]

To pytanie ma już odpowiedź tutaj:

Szukałem wszędzie i nie mogę znaleźć solidnej odpowiedzi. Zgodnie z dokumentacją Java rzuca Javę.lang.Błąd StackOverflowError {[5] } w następującej sytuacji:

Rzucony, gdy występuje przepełnienie stosu, ponieważ aplikacja rekursuje się zbyt głęboko.

Ale to rodzi dwa pytania:

  • czy nie ma innych sposobów na przepełnienie stosu, nie tylko poprzez rekurencję?
  • Czy StackOverflowError dzieje się przed JVM faktycznie przepełnia stos lub po?

Aby rozwinąć drugie pytanie:

Kiedy Java rzuca StackOverflowError, czy można bezpiecznie założyć, że stos nie zapisał się do sterty? Jeśli zmniejszysz rozmiar stosu lub sterty w try / catch na funkcji, która rzuca przepełnienie stosu, można kontynuować pracę? Czy to jest gdzieś udokumentowane?

Odpowiedzi nie szukam:

  • StackOverflow dzieje się z powodu złej rekurencji.
  • przepływ stosu ma miejsce, gdy stos spotyka się ze stosem.
Author: hichris123, 2014-03-05

10 answers

Wydaje się, że myślisz, że błąd stackoverflow jest jak wyjątek przepełnienia bufora w programach natywnych, gdy istnieje ryzyko zapisu do pamięci, która nie została przydzielona do bufora, a tym samym uszkodzenia niektórych innych miejsc pamięci. Wcale tak nie jest.

JVM ma podaną pamięć przydzieloną dla każdego stosu każdego wątku i jeśli dojdzie do próby wywołania metody wypełniającej tę pamięć, JVM wyświetli błąd. Tak jak by to było, gdybyś próbował napisać przy indeksie N tablicy długości N. nie może dojść do uszkodzenia pamięci. Stos nie może zapisywać na stercie.

StackOverflowError jest dla stosu tym, czym OutOfMemoryError dla stosu: po prostu sygnalizuje, że nie ma więcej dostępnej pamięci.

Opis Z błędów maszyny Wirtualnej (§6.3)

StackOverflowError: w implementacji maszyny wirtualnej Java zabrakło miejsca na stos dla wątku, zazwyczaj dlatego, że wątek wykonuje Nieograniczona liczba rekurencyjnych wywołań w wyniku błędu w programie wykonującym.

 195
Author: JB Nizet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-04 22:37:58

Czy nie istnieją inne sposoby na przepełnienie stosu, nie tylko poprzez rekurencję?

Jasne. Po prostu ciągle dzwoń do metod, bez powrotu. Będziesz jednak potrzebował wielu metod, chyba że zezwolisz na rekurencję. W rzeczywistości nie ma to znaczenia: ramka stosu jest ramką stosu, niezależnie od tego, czy jest jedną z metod rekurencyjnych, czy nie, jest taka sama.

ODPOWIEDŹ na drugie pytanie brzmi: przepływ stosu jest wykrywany, gdy JVM próbuje przydzielić ramkę stosu do następnego połączenia, i stwierdzi, że nie jest to możliwe. Więc nic nie zostanie nadpisane.

 52
Author: Ingo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-04 20:55:12

Czy nie ma innych sposobów na przepełnienie stosu, nie tylko przez rekurencję?

Wyzwanie przyjęte :) StackOverflowError bez rekurencji (wyzwanie nie powiodło się, Zobacz komentarze):

public class Test
{
    final static int CALLS = 710;

    public static void main(String[] args)
    {
        final Functor[] functors = new Functor[CALLS];
        for (int i = 0; i < CALLS; i++)
        {
            final int finalInt = i;
            functors[i] = new Functor()
            {
                @Override
                public void fun()
                {
                    System.out.print(finalInt + " ");
                    if (finalInt != CALLS - 1)
                    {
                        functors[finalInt + 1].fun();
                    }
                }
            };
        }
        // Let's get ready to ruuuuuuumble!
        functors[0].fun(); // Sorry, couldn't resist to not comment in such moment. 
    }

    interface Functor
    {
        void fun();
    }
}

Skompiluj ze standardem javac Test.java i uruchom z java -Xss104k Test 2> out. Następnie more out powie Ci:

Exception in thread "main" java.lang.StackOverflowError

Druga próba. Teraz pomysł jest jeszcze prostszy. Primitives w Javie mogą być przechowywane na stosie. Więc zadeklarujmy wiele sobowtórów, jak double a1,a2,a3.... To skrypt może napisać, skompilować i uruchomić kod dla nas:
#!/bin/sh

VARIABLES=4000
NAME=Test
FILE=$NAME.java
SOURCE="public class $NAME{public static void main(String[] args){double "
for i in $(seq 1 $VARIABLES);
do
    SOURCE=$SOURCE"a$i,"
done
SOURCE=$SOURCE"b=0;System.out.println(b);}}"
echo $SOURCE > $FILE
javac $FILE
java -Xss104k $NAME

I... Mam coś nieoczekiwanego:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f4822f9d501, pid=4988, tid=139947823249152
#
# JRE version: 6.0_27-b27
# Java VM: OpenJDK 64-Bit Server VM (20.0-b12 mixed mode linux-amd64 compressed oops)
# Derivative: IcedTea6 1.12.6
# Distribution: Ubuntu 10.04.1 LTS, package 6b27-1.12.6-1ubuntu0.10.04.2
# Problematic frame:
# V  [libjvm.so+0x4ce501]  JavaThread::last_frame()+0xa1
#
# An error report file with more information is saved as:
# /home/adam/Desktop/test/hs_err_pid4988.log
#
# If you would like to submit a bug report, please include
# instructions how to reproduce the bug and visit:
#   https://bugs.launchpad.net/ubuntu/+source/openjdk-6/
#
Aborted
Jest w 100% powtarzalny. Jest to związane z drugim pytaniem:

Czy StackOverflowError dzieje się zanim JVM faktycznie przepełni stos czy po?

Tak więc, w przypadku OpenJDK 20.0-b12 widzimy, że JVM po raz pierwszy eksplodował. Ale to chyba błąd, może ktoś to potwierdzi w komentarzach proszę, bo nie jestem jasne. Mam to zgłosić? Może jest już naprawiony w nowszej wersji... Zgodnie z JVM specification link (podany przez JB Nizet w komentarzu) JVM powinien rzucać StackOverflowError, a nie umierać:

Jeśli obliczenia w wątku wymagają większej Wirtualnej Maszyny Java stosu niż jest to dozwolone, wirtualna maszyna Javy rzuca StackOverflowError.


Trzecia próba.
public class Test {
    Test test = new Test();

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

Chcemy utworzyć nowy obiekt Test. Więc jego (implicit) konstruktor zostanie wywołany. Ale tuż przed tym, wszyscy członkowie Test są inicjalizowani. Więc Test test = new Test() jest wykonywane jako pierwsze...

Chcemy utworzyć nowy obiekt Test...

Update: pech, to jest rekurencja, zadałem pytanie o totutaj .

 27
Author: Adam Stelmaszczyk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:59

Najczęstszą przyczyną StackOverFlowError jest nadmiernie głęboka lub nieskończona rekurencja.

Na przykład:

public int yourMethod(){
       yourMethod();//infinite recursion
}

W Języku Java:

Istnieją two obszary w pamięci sterty i stosu. stack memory jest używany do przechowywania zmiennych lokalnych i wywołania funkcji, podczas gdy {[3] } jest używany do przechowywania obiektów w Javie.

Jeśli w stosie nie ma pamięci do przechowywania wywołania funkcji lub zmiennej lokalnej, JVM wyrzuci java.lang.StackOverFlowError

While if there is no more stop space for creating obiekt, JVM rzuci java.lang.OutOfMemoryError

 4
Author: Lazy Coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-06 14:00:16

Nie ma "StackOverFlowException". Masz na myśli "StackOverFlowError".

Tak, możesz kontynuować pracę, jeśli ją złapiesz, ponieważ stos jest wyczyszczony, gdy to zrobisz, ale byłoby to zła i brzydka opcja.

Kiedy dokładnie błąd zostanie wyrzucony ? - Po wywołaniu metody i JVM sprawdza, czy jest wystarczająco dużo pamięci, aby to zrobić. Oczywiście błąd jest wyrzucany, jeśli nie jest to możliwe.

  • nie, tylko w ten sposób możesz uzyskać ten błąd: zapełnić swój stos. Ale nie tylko poprzez rekursję, również wywołanie metod, które w nieskończoność wywołują inne metody. To bardzo konkretny błąd, więc nie.
  • jest wyrzucany przed zapełnieniem stosu, dokładnie podczas weryfikacji. Gdzie można umieścić dane, jeśli nie ma wolnego miejsca ? Zastępowanie innych ? Nie.
 3
Author: GabrielBB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-04 21:14:31

Są dwa główne miejsca, w których rzeczy mogą być przechowywane w Javie. Pierwszym z nich jest sterta, która służy do dynamicznego przydzielania obiektów. new.

Dodatkowo każdy uruchomiony wątek otrzymuje swój własny stos i otrzymuje ilość pamięci przydzielonej do tego stosu.

Gdy wywołujesz metodę, dane są wypychane do stosu, aby zarejestrować wywołanie metody, przekazywane parametry i przydzielane zmienne lokalne. Metoda z pięcioma zmiennymi lokalnymi i trzema parametrami będzie używać będzie więcej miejsca na stosie niż metoda void doStuff() bez zmiennych lokalnych.

Główne zalety stosu to brak fragmentacji pamięci, wszystko dla jednego wywołania metody jest przydzielane na wierzchu stosu, a powrót z metod jest łatwy. Aby powrócić z metody wystarczy cofnąć stos do poprzedniej metody, ustawić dowolną wartość potrzebną do wartości zwracanej i gotowe.

Ponieważ stos jest stałym rozmiarem na wątek, (zauważ, że specyfikacja Java nie wymagaj stałego rozmiaru, ale większość implementacji JVM w momencie pisania używa stałego rozmiaru) i ponieważ miejsce na stosie jest potrzebne za każdym razem, gdy wykonujesz wywołanie metody, miejmy nadzieję, że teraz powinno być jasne, dlaczego może się wyczerpać i co może spowodować jej wyczerpanie. Nie ma ustalonej liczby wywołań metod, nie ma nic konkretnego na temat rekurencji, dostajesz wyjątek, który próbujesz wywołać metodę i nie ma wystarczającej ilości pamięci.

Oczywiście wielkość stosów jest ustawiona na tyle wysoko, że jest bardzo mało prawdopodobne, aby stało się to w normalnym kodzie. W kodzie rekurencyjnym może być dość łatwo rekurencyjnie do ogromnych głębokości i w tym momencie zaczynasz napotkać ten błąd.

 3
Author: Tim B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-14 09:07:07

StackOverflowError występuje z powodu zbyt głębokiego rekursowania aplikacji(nie jest to odpowiedź, której oczekujesz).

Inne rzeczy, które przydarzają się StackOverflowError, to ciągłe wywoływanie metod z metod, dopóki nie uzyskasz StackOverflowError, ale nikt nie może programować, aby uzyskać StackOverflowErrori nawet jeśli programista tak robi, to nie przestrzega standardów kodowania cyclomatic complixity, które każdy programista musi zrozumieć podczas programowania. Taki powód "StackOverflowError" będzie wymagał dużo czasu, aby napraw to.

Ale nieświadome kodowanie jednej lub dwóch linii, które powoduje StackOverflowError jest zrozumiałe i JVM rzuca to i możemy to natychmiast naprawić. tutaj jest moja odpowiedź ze zdjęciem na inne pytanie.

 2
Author: AmitG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:36

Przepływ stosu ma miejsce, gdy wywołanie funkcji jest wywołane i stos jest pełny.

Podobnie jak ArrayOutOfBoundException. Nie może niczego zepsuć, w rzeczywistości jest bardzo możliwe, aby go złapać i odzyskać.

Zwykle dzieje się to w wyniku niekontrolowanej rekurencji, ale może być również spowodowane przez po prostu posiadanie bardzo głębokiego stosu wywołań funkcji.

 0
Author: njzk2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-04 20:54:57

W c# można osiągnąć przepełnienie stosu w inny sposób, błędnie definiując właściwości obiektu. Na przykład:

private double hours;

public double Hours
        {
            get { return Hours; }
            set { Hours = value; }
        }

Jak widzisz, to na zawsze będzie zwracać godziny z wielką literą H, która sama w sobie zwróci godziny i tak dalej.

Przepełnienie stosu często występuje również z powodu braku pamięci lub podczas używania języków zarządzanych, ponieważ menedżer języków (CLR, JRE) wykryje, że kod utknął w nieskończonej pętli.

 0
Author: Bryan Arbelo - MaG3Stican,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-11 20:51:08

Ale to rodzi dwa pytania:

  1. czy nie ma innych sposobów na przepełnienie stosu, nie tylko poprzez rekurencję?
  2. czy StackOverflowError dzieje się przed JVM faktycznie przepełnia stos lub po?
  1. Może również wystąpić, gdy przydzielamy rozmiar większy niż limit stosu (np. int x[10000000];).

  2. Odpowiedź na drugie to

Każdy wątek ma swój własny stos, który zawiera ramkę dla każda metoda wykonywana w tym wątku. Tak więc aktualnie wykonywana metoda znajduje się na górze stosu. Nowa ramka jest tworzona i dodawana (wypychana) na górę stosu dla każdego wywołania metody. Ramka jest usuwana (popped), gdy metoda powraca normalnie lub gdy podczas wywołania metody zostanie rzucony nieobciążony wyjątek. Stos nie jest bezpośrednio manipulowany, z wyjątkiem obiektów ramki push i pop, dlatego obiekty ramki mogą być przydzielane w stercie i pamięć nie musi być przylegające.

Więc rozważając stack w wątku możemy stwierdzić.

Stos może mieć rozmiar dynamiczny lub stały. Jeśli wątek wymaga większego stosu niż dozwolony, wyrzuca się StackOverflowError. Jeśli wątek wymaga nowej ramki i nie ma wystarczającej ilości pamięci, aby ją przydzielić, to rzuca się OutOfMemoryError.

Opis JVM znajdziesz tutaj

 -1
Author: eatSleepCode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-05 11:35:48