Я удивлен тем, как можно продолжить выполнение даже после того, как StackOverflowError
в Java произошло.
Я знаю, что StackOverflowError
это подкласс класса Error. Класс 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 стек должен быть настолько заполнен, чтобы не было места для вызова другой функции. Работает ли блок обработки ошибок в другом стеке или что здесь происходит?
источник
Ответы:
Когда стек переполняется и
StackOverflowError
выбрасывается, обычная обработка исключений раскручивает стек. Раскрутка стопки означает:... пока не будет обнаружено исключение. Это нормально (фактически, необходимо) и не зависит от того, какое исключение выбрано и почему. Поскольку вы перехватываете исключение за пределами первого вызова
foo()
, все тысячиfoo
кадров стека, заполнившие стек, были размотаны, и большая часть стека может быть использована снова.источник
foo
завершается неопределенным состоянием, поэтому любой объект, которого он мог коснуться, должен считаться сломанным. Поскольку вы не знаете, в какой функции произошло переполнение стека, только то, что он должен быть потомкомtry
блока, который его поймал, любой объект, который может быть изменен любым доступным оттуда методом, теперь подозревается. Обычно не стоит выяснять, что случилось, и пытаться это исправить.Error
s нельзя предвидеть даже при написании кода, безопасного для исключений.Error
с собой. OP спрашивает только о томStackOverflowError
, и он спрашивает конкретную вещь об обработке этой ошибки: как может вызов метода не потерпеть неудачу при обнаружении этой ошибки.Когда выбрасывается StackOverflowError, стек заполнен. Однако когда он перехватывается , все эти
foo
вызовы выталкиваются из стека.bar
может работать нормально, потому что стек больше не переполняетсяfoo
s. (Обратите внимание, что я не думаю, что JLS гарантирует, что вы сможете восстановиться после такого переполнения стека.)источник
Когда происходит StackOverFlow, JVM выскакивает на защелку, освобождая стек.
В вашем примере он избавляется от всех сложенных файлов foo.
источник
Потому что стек на самом деле не переполняется. Лучшее имя может быть AttemptToOverflowStack. В основном это означает, что последняя попытка настроить фрейм стека дает ошибку, потому что в стеке недостаточно свободного места. На самом деле в стеке может быть много места, но недостаточно места. Таким образом, какая бы операция ни зависела от успешного выполнения вызова (обычно это вызов метода), она никогда не выполняется, и все, что остается программе, - это иметь дело с этим фактом. Это означает, что это действительно ничем не отличается от любого другого исключения. Фактически, вы можете поймать исключение в функции, выполняющей вызов.
источник
Как уже было сказано, после перехвата a можно выполнять код и, в частности, вызывать функции,
StackOverflowError
поскольку обычная процедура обработки исключений JVM раскручивает стек между точкамиthrow
иcatch
точками, освобождая пространство стека для использования. И ваш эксперимент подтверждает это.Однако, это не совсем то же самое , как сказать , что это, в общем, можно восстановить из
StackOverflowError
.StackOverflowError
IS-AVirtualMachineError
, который IS-ANError
. Как вы отметили, Java дает несколько расплывчатых советов дляError
:и вы, резонно заключить , что должно звучит как ловить
Error
может быть хорошо в некоторых обстоятельствах. Обратите внимание, что выполнение одного эксперимента не демонстрирует, что что-то в целом безопасно. Это могут сделать только правила языка Java и спецификации используемых вами классов. AVirtualMachineError
- это особый класс исключения, поскольку спецификация языка Java и спецификация виртуальной машины Java предоставляют информацию о семантике этого исключения. В частности, последний говорит :...
Ключевой проблемой является то, что вы «не можете предсказать», где и когда
StackOverflowError
будет брошен. Нет никаких гарантий, куда он не будет брошен. Например, вы не можете полагаться на то, что он будет брошен при входе в метод. Его можно бросить в точку внутри метода.Эта непредсказуемость потенциально губительна. Поскольку он может быть брошен внутри метода, он может быть брошен частично через последовательность операций, которые класс считает одной «атомарной» операцией, оставляя объект в частично измененном, несовместимом состоянии. Когда объект находится в несовместимом состоянии, любая попытка использовать этот объект может привести к ошибочному поведению. Во всех практических случаях вы не можете знать, какой объект находится в несогласованном состоянии, поэтому вы должны предположить, что ни один объект не заслуживает доверия. Следовательно, любая операция восстановления или попытка продолжить после обнаружения исключения могут иметь ошибочное поведение. Поэтому единственное безопасное решение - не поймать
StackOverflowError
, а скорее позволить программе завершить работу. (На практике вы можете попытаться вести журнал ошибок, чтобы помочь в устранении неполадок, но вы не можете полагаться на правильную работу этого журнала). То есть из файлаStackOverflowError
.источник