Почему можно восстановить из StackOverflowError?

Я удивлен тем, как можно продолжить выполнение даже после того, как StackOverflowError произошел в Java.

Я знаю, что StackOverflowError является подслоем класса Error. Ошибка класса декомментируется как "подкласс Throwable, который указывает на серьезные проблемы, которые разумное приложение не должно пытаться поймать".

Это больше похоже на рекомендацию, чем на правило, в котором подчеркивается, что нахождение ошибки, например, StackOverflowError, фактически разрешено, и до разумности программиста это сделать не следует. И посмотрите, я проверил этот код, и он нормально завершается.

public class Test
{
    public static void main(String[] args)
    {
        try {
            foo();
        } catch (StackOverflowError e) {
            bar();
        }
        System.out.println("normal termination");
    }

    private static void foo() {
        System.out.println("foo");
        foo();
    }

    private static void bar() {
        System.out.println("bar");
    }
}

Как это может быть? Я думаю, что к тому времени, когда StackOverflowError будет сброшен, стек должен быть настолько полным, что нет места для вызова другой функции. Является ли блок обработки ошибок запущенным в другом стеке или что здесь происходит?

Ответ 1

При переполнении стека и StackOverflowError обычная обработка исключений разворачивает стек. Разматывание стека означает:

  • прервать выполнение текущей активной функции
  • удалите его стек стека, перейдите к вызывающей функции
  • прервать выполнение вызывающего абонента
  • удалите его стек стека, перейдите к вызывающей функции
  • и т.д.

... пока не будет обнаружено исключение. Это нормально (на самом деле, необходимо) и независимо от того, какое исключение выбрано и почему. Поскольку вы обнаруживаете исключение за пределами первого вызова foo(), тысячи кадров стека foo, которые заполняют стек, все разматывались, и большая часть стека была бесплатна для использования снова.

Ответ 2

Когда StackOverflowError выбрасывается, стек заполняется. Однако, когда он поймал, все эти вызовы foo были выведены из стека. bar может работать нормально, потому что стек больше не переполняется с помощью foo s. (Обратите внимание, что я не думаю, что JLS гарантирует, что вы можете восстановить из, как это.)

Ответ 3

Когда происходит StackOverFlow, JVM выскочит до улова, освободив стек.

В вашем примере, он получает rids из всех stacked foo.

Ответ 4

Потому что стек фактически не переполняется. Лучшее имя может быть AttemptToOverflowStack. В основном это означает, что последняя попытка отрегулировать фрейм стека заблуждается, потому что в стеке недостаточно свободного места. У стека действительно может быть много свободного места, просто недостаточно места. Таким образом, любая операция зависела бы от успешного вызова (как правило, вызова метода), никогда не вынимается, и все, что осталось, - это программа, чтобы справиться с этим фактом. Это означает, что он ничем не отличается от любого другого исключения. Фактически, вы можете поймать исключение в функции, выполняющей вызов.