В каком случае в Java возникает StackOverflowError? Приведите пример кода.

«В каком случае в Java возникает StackOverflowError? Приведите пример кода.» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

StackOverflowError — это ошибка времени выполнения (java.lang.VirtualMachineError), которая возникает при переполнении стека вызовов (call stack). Стек вызовов — это область памяти, которая хранит информацию о цепочке вызванных методов (локальные переменные, адреса возврата). Чаще всего это происходит из-за бесконечной или слишком глубокой рекурсии, когда метод вызывает сам себя без достижения базового условия выхода.

Классический пример с бесконечной рекурсией:

public class StackOverflowDemo {

    // Рекурсивный метод без условия остановки
    public static void infiniteRecursion() {
        infiniteRecursion(); // Каждый вызов добавляет новый фрейм в стек
    }

    public static void main(String[] args) {
        infiniteRecursion(); // Приведет к StackOverflowError
    }
}

Вывод в консоли:

Exception in thread "main" java.lang.StackOverflowError
    at StackOverflowDemo.infiniteRecursion(StackOverflowDemo.java:5)
    at StackOverflowDemo.infiniteRecursion(StackOverflowDemo.java:5)
    ... (тысячи повторяющихся строк)

Более реалистичный пример с ошибкой в условии выхода:

public class Factorial {
    // Ошибочная реализация: условие выхода никогда не достигнуто (n всегда > 0)
    public static int faultyFactorial(int n) {
        if (n > 0) { // Должно быть if (n == 0) return 1;
            return n * faultyFactorial(n - 1);
        }
        // При n = 5, 4, 3, 2, 1, 0, -1, -2... рекурсия уйдет в минус бесконечно.
        return 1;
    }

    public static void main(String[] args) {
        // Вызовет StackOverflowError, так как рекурсия никогда не достигнет n <= 0 корректно.
        System.out.println(faultyFactorial(5));
    }
}

Как избежать StackOverflowError:

  1. Всегда определять корректное базовое условие в рекурсивных функциях.
  2. Убедиться, что рекурсия сходится, т.е. каждый шаг приближает к базовому случаю.
  3. Рассмотреть итеративную альтернативу для очень глубоких вычислений.
  4. Увеличить размер стека JVM с помощью параметра -Xss (например, -Xss2m), но это не решение логической ошибки, а временная мера.

Правильная реализация факториала:

public static int correctFactorial(int n) {
    if (n == 0 || n == 1) { // Четкое базовое условие
        return 1;
    }
    return n * correctFactorial(n - 1); // Шаг, гарантированно уменьшающий n
}