StackOverflowError
Spis treści
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.
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
Dyskusja i komentarze
Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.