Baza Wiedzy

StackOverflowError

Opis🔗

W ramach pamięci wirtualnej maszyny Javy możemy wyróżnić m.in. takie obszary jak stos i sterta. Na stosie odkładane są ramki odpowiadające wywołaniu kolejnych metod w ramach danego wątku, natomiast na stertę trafiają tworzone w ramach aplikacji obiekty. StackOverflowError jest błędem, który występuje wtedy, gdy na stosie aktualnie wykonywanego wątku brakuje miejsca. Klasa błędu znajduje się w pakiecie java.lang, no i właśnie warto zwrócić uwagę na to, że jest to błąd, a nie wyjątek, tzn. klasa StackOverflowError nie dziedziczy ani po klasie Exception, ani RuntimeException.

 

Przykład🔗

Błąd ten najczęściej występuje w sytuacji, gdy w ramach aplikacji wywoływana jest metoda rekurencyjna, która została zapisana w błędy sposób, czyli np. posiada błędnie zdefiniowany warunek stopu lub liczba wywołań rekurencyjnych jest tak duża, że na stosie po prostu brakuje miejsca. Do błędu można doprowadzić szczególnie łatwo w przypadku algorytmów charakteryzujących się wykładniczą złożonością obliczeniową, np. obliczaniem ciągu fibonacciego.

Do błędu można jednak doprowadzić także w przypadku prostej metody rekurencyjnej z liniową złożonością. Poniżej zapisana jest metoda, która oblicza sumę liczb z przedziału od 1 do N.

static int sum(int limit) {
    return limit >= 0 ? limit + sum(limit - 1) : 0;
}

Przykładowo dla argumentu 5 zwraca 15, ponieważ 5+4+3+2+1 = 15.

Kurs Java

Wywołanie tej metody dla argumentów takich jak 100, czy 1000 nie powinno stanowić problemu i w wyniku zobaczymy poprawne wydruki:

class StackOverflowExample {
    public static void main(String[] args) {
        int sum = sum(1000);
        System.out.println(sum);
    }

    static int sum(int limit) {
        return limit >= 1 ? limit + sum(limit - 1) : limit;
    }
}

Jeżeli jednak tę samą metodę wywołasz z dużo większym argumentem, np. 1_000_000

class StackOverflowExample {
    public static void main(String[] args) {
        int sum = sum(1_000_000);
        System.out.println(sum);
    }

    static int sum(int limit) {
        return limit >= 1 ? limit + sum(limit - 1) : limit;
    }
}

To uruchomienie programu zakończy się komunikatem błędu Exception in thread "main" java.lang.StackOverflowError.

 

Naprawa błędu🔗

Sposobem na wyeliminowanie problemu jest najczęściej optymalizacja zapisanego algorytmu. W Javie niestety nie można wykorzystać rekurencji ogonowej w celu ograniczenia rozmiaru stosu, dlatego jedynym sposobem na rozwiązanie problemu jest przerobienie wywołań rekurencyjnych na klasyczną formę iteracyjną. Zapisując wcześniejszy kod w taki sposób:

static int sum(int limit) {
    int sum = 0;
    for (int i = 1; i <= limit; i++) {
        sum += i;
    }
    return sum;
}

lub

static int sum(int limit) {
    return IntStream.rangeClosed(1, limit).sum();
}

Metoda zwróci wynik dla dowolnie dużego argumentu.

 

Dodatkowe źródła

Dokumentacja klasy StackOverflowError: https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/StackOverflowError.html

Najlepszy newsletter o Javie w Polsce

Czy chcesz otrzymywać nowości ze świata Javy oraz przykładowe pytania rekrutacyjne? Zapisz się na newsletter i bądź na bieżąco! Otrzymasz także ekskluzywne materiały oraz informacje o nowych kursach i promocjach.

Nikomu nie udostępniamy Twojego maila, a jeśli zechcesz to w każdej chwili możesz się wypisać.

Komentarze do artykułu

Wyłączyliśmy możliwość dodawania komentarzy. Poniżej znajdziesz archiwalne wpisy z czasów gdy strona była jeszcze hobbystycznym blogiem. Zapraszamy natomiast do zadawnia pytań i dyskusji na naszej grupe na facebooku.