Можно ли когда-нибудь поймать StackOverflowError в Java?

27

Раньше я думал, что это не так, но вчера я должен был это сделать. Это приложение, которое использует Akka (реализация системы акторов для JVM) для обработки асинхронных заданий. Один из актеров выполняет некоторые манипуляции с PDF, и поскольку библиотека глючит, время от времени она умирает StackOverflowError.

Второй аспект заключается в том, что Akka настроен на отключение всей системы акторов в случае обнаружения фатальной ошибки JVM (например, StackOverflowError).

Третий аспект заключается в том, что эта система акторов встроена в веб-приложение (по причинам WTF, унаследованным и другим причинам), поэтому, когда система акторов закрыта, веб-приложение не отключается. Чистый эффект заключается в том, что StackOverflowErrorнаше приложение для обработки заданий становится просто пустым веб-приложением.

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

Кроме того, позвольте мне отметить, что я прежде всего за исправление ошибки (на самом деле я уже исправил SOE в этом же приложении несколько дней назад), но я действительно не знаю, когда это Такая ситуация может возникнуть.

Почему было бы лучше перезапустить процесс JVM, а не перехватывать StackOverflowError, помечать эту работу как невыполненную и продолжать свою работу?

Есть ли веская причина никогда не ловить ГП? За исключением «лучших практик», это неопределенный термин, который мне ничего не говорит.

Ionuț G. Stan
источник
1
другим вариантом было бы увеличить пространство стека, доступное на JVM
ratchet freak
3
@ratchetfreak: StackOverflowExceptions обычно происходят из-за неразрывной цепочки вызовов методов - увеличение места в стеке увеличит стоимость памяти нового потока без какой-либо выгоды.
'59
1
По крайней мере одна SOE была законной, потому что ввод был очень большим. К сожалению, обрабатывать его с помощью рекурсивной реализации (подразумевается регулярное выражение Java) было не очень хорошей идеей. В любом случае, даже если вычисления гарантированно завершатся, вы не знаете, достаточно ли велик новый размер стека для других вычислений.
Ionuț G. Stan
2
Разве это не должно быть перенесено в Ста ... О, подожди ... не бери в голову. :-)
Blrfl
По поводу вашей глючной библиотеки. Вы действительно должны перенести эту pdf-функцию в свой собственный процесс, чтобы позволить операционной системе убить ее.
Эсбен Сков Педерсен

Ответы:

44

Как правило, если бы это было абсолютно никогда, приемлемо что-либо делать, и было достигнуто согласие по этому поводу, разработчики языка не допустили бы этого. Таких единогласно четко сформулированных изречений почти нет. (К счастью, это то, что удерживает нас, программистов, на работе!)

Очень похоже, что вы нашли ситуацию, в которой перехват этой ошибки - лучший вариант для вас: он позволяет вашему приложению работать, а все остальные альтернативы - нет, и в итоге это важно. Все «лучшие практики» являются просто суммированием многолетнего опыта со многими случаями, который обычно можно использовать вместо детального анализа конкретного случая, чтобы сэкономить время; в вашем случае вы уже провели конкретный анализ и получили другой результат. Поздравляю, вы способны к самостоятельной мысли!

(Тем не менее, безусловно, существуют ситуации, когда переполнение стека может сделать приложение непоследовательным, как истощение памяти. Просто представьте, что какой-то объект создается и затем инициализируется с помощью вложенных внутренних вызовов методов - если один из них выдает объект, объект вполне может быть в состоянии, которое, как предполагается, невозможно, как если бы распределение было неудачным. Но это не значит, что ваше решение все еще не может быть лучшим.)

Килиан Фот
источник
3
Спасибо. Мои сомнения несколько StackOverflowExceptionусилились после того, как я узнал, что .NET сделал неуловимое исключение. Я знаю, это другая платформа, но я подумал, что у них могла быть причина. Кроме того, ваша точка зрения относительно инициализации объекта является точной. Это заставляет меня думать, что я должен поймать эту SOE несколькими уровнями абстракции ниже, чтобы я не поймал «неправильную» SOE.
Пусть Г. Стэн
14
+1: лучшие практики всегда должны сопровождаться объяснениями, почему и в каком контексте они являются «лучшими», чтобы вы могли судить, применимы ли они к вашему конкретному делу.
Майкл Боргвардт
situations hereдолжно быть situations where.
Обслуживание
2

Я не знаю, есть ли здесь какие-либо специфичные для JVM риски, но в целом это кажется вполне разумным.

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

Худший случай - редкий, и вряд ли произойдет снова, если вы перезапустите сортировку на частично отсортированном наборе, поэтому имеет смысл перехватить исключение переполнения стека, когда это происходит, и перезапустить работу, вместо того, чтобы предотвратить возникновение ошибки или ее уничтожение. все приложение.

Корнель
источник