Что такое StackOverflowError?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое 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, поэтому его не рекомендуется ловить.