← Назад к вопросам

Что такое StackOverflowError?

1.2 Junior🔥 181 комментариев
#JVM и управление памятью#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое StackOverflowError

StackOverflowError - это ошибка, которая возникает, когда стек вызовов переполняется. Это происходит при глубокой рекурсии или бесконечных циклах вызовов функций. StackOverflowError наследуется от Error, а не Exception, и не рекомендуется её ловить.

Как это работает

Каждый раз, когда функция вызывает другую функцию, Java создаёт новый frame в стеке вызовов (call stack). Каждый frame занимает память. Если рекурсия слишком глубокая, всё доступное место в стеке закончится, и выбросится StackOverflowError.

// Пример 1: Прямая рекурсия без базового условия
public long factorial(int n) {
    return n * factorial(n - 1);  // ОШИБКА: нет базового условия!
}

// При вызове factorial(5):
// factorial(5)
//   -> factorial(4)
//     -> factorial(3)
//       -> factorial(2)
//         -> factorial(1)
//           -> factorial(0)
//             -> factorial(-1)
//               -> factorial(-2)
//                 -> ... бесконечно!

// Результат: java.lang.StackOverflowError

Правильная рекурсия

// Хорошо: есть базовое условие (base case)
public long factorial(int n) {
    if (n <= 1) {
        return 1;  // Базовый случай - рекурсия заканчивается
    }
    return n * factorial(n - 1);
}

// factorial(5) = 5 * factorial(4)
//              = 5 * 4 * factorial(3)
//              = 5 * 4 * 3 * factorial(2)
//              = 5 * 4 * 3 * 2 * factorial(1)
//              = 5 * 4 * 3 * 2 * 1 = 120

Пример 2: Взаимная рекурсия

public int methodA(int n) {
    if (n == 0) return 0;
    return methodB(n - 1);
}

public int methodB(int n) {
    if (n == 0) return 0;
    return methodA(n - 1);
}

// methodA(1000) выбросит StackOverflowError
// Потому что: A -> B -> A -> B -> A -> ...

Пример 3: Циклическая зависимость

public class Node {
    public Node getParent() {
        return parent;  // parent указывает на this (циклический граф)
    }
}

// Если в графе есть цикл, и мы рекурсивно обходим граф
public void traverse(Node node, Set<Node> visited) {
    if (visited.contains(node)) return;
    visited.add(node);
    
    for (Node child : node.getChildren()) {
        traverse(child, visited);  // Может привести к StackOverflowError
    }
}

Как исправить

1. Добавить базовое условие (base case)

// Плохо: нет базового условия
public void printNumbers(int n) {
    System.out.println(n);
    printNumbers(n + 1);  // Бесконечно
}

// Хорошо: есть базовое условие
public void printNumbers(int n) {
    if (n > 100) return;  // Базовое условие
    System.out.println(n);
    printNumbers(n + 1);
}

2. Использовать итерацию вместо рекурсии

// Рекурсивно (опасно при больших n)
public long factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

// Итеративно (безопасно)
public long factorial(int n) {
    long result = 1;
    for (int i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}

// factorial(1000000) работает итеративно, но выбросит StackOverflowError рекурсивно

3. Увеличить размер стека (не рекомендуется)

# При запуске приложения можно увеличить стек
java -Xss2m -jar myapp.jar
# -Xss2m = 2 мегабайта для стека потока

# Но это не решает проблему, только откладывает её

4. Использовать Set для отслеживания посещённых узлов

public void traverse(Node node, Set<Node> visited) {
    if (visited.contains(node)) return;
    visited.add(node);
    
    for (Node child : node.getChildren()) {
        traverse(child, visited);  // Циклы больше не проблема
    }
}

StackOverflowError vs OutOfMemoryError

// StackOverflowError - переполнение стека вызовов (неглубокая память)
recursiveFunction();  // слишком глубокая рекурсия

// OutOfMemoryError - нет памяти в heap (где объекты)
List<Object> list = new ArrayList<>();
while (true) {
    list.add(new Object());  // Бесконечно добавляем объекты
}

Почему это Error, а не Exception

// StackOverflowError наследуется от Error
public class StackOverflowError extends Error { }

// Это значит, что его НЕЛЬЗЯ ловить и обрабатывать
try {
    recursiveFunction();
} catch (StackOverflowError e) {
    // Это плохая практика! Error не рекомендуется ловить
    // Если произошла StackOverflowError, приложение уже в плохом состоянии
}

// Exception - это то, что можно ловить и обрабатывать
// Error - это серьёзные проблемы, которые приложение не должно обрабатывать

Резюме

StackOverflowError возникает при слишком глубокой рекурсии. Главное правило: любая рекурсия ДОЛЖНА иметь базовое условие (base case). Когда рекурсия слишком глубокая, используйте итерацию вместо неё. StackOverflowError - это Error, а не Exception, поэтому его не рекомендуется ловить.