Когда ловить java.lang.Error?

Ответы:

101

Как правило, никогда.

Однако иногда нужно отловить конкретные ошибки.

Если вы пишете код, похожий на фреймворк (загрузка сторонних классов), было бы разумно поймать LinkageError(определение класса не найдено, неудовлетворенная ссылка, несовместимое изменение класса).

Я также видел несколько глупых сторонних кодов, генерирующих подклассы Error, так что вам тоже придется справиться с ними.

Кстати, я не уверен, что от этого невозможно вылечиться OutOfMemoryError.

Йони Ройт
источник
3
То, что я должен был сделать, чтобы загрузить библиотеки DLL, не удалось бы, если бы они были неправильно настроены. Не является фатальной ошибкой для этого приложения.
Марио Ортегон,
7
Иногда имеет смысл перехватить OutOfMemoryError - например, когда вы создаете большие списки массивов.
SpaceTrucker
3
@SpaceTrucker: хорошо ли этот подход работает в многопоточных приложениях, или существует значительный риск того, что из-за этого не удастся выделить меньшее количество ресурсов в других потоках? … Предположительно только в том случае, если ваши массивы были достаточно малы, чтобы их можно было разместить, но ничего не оставляли для кого-либо.
PJTraill
@PJTraill Я не уверен в этом. Это потребует некоторых реальных статистических выборок. Я думал, что видел такой код, но не могу вспомнить, где он был.
SpaceTrucker,
51

Никогда. Никогда нельзя быть уверенным, что приложение сможет выполнить следующую строку кода. Если вы его получите OutOfMemoryError, у вас нет гарантии, что вы сможете сделать что-либо надежно . Поймать RuntimeException и проверить исключения, но не ошибки.

http://pmd.sourceforge.net/rules/strictexception.html

tronda
источник
27
Никогда не говори никогда. у нас есть тестовый код, который делает «assert false»; затем перехватывает AssertionError, чтобы убедиться, что установлен флаг -ea. Кроме этого ... да, вероятно, никогда ;-)
Outlaw Programmer
3
Как насчет серверного приложения, которое передает запросы рабочим потокам. Может быть, нет смысла перехватывать Throwable в рабочем потоке, чтобы перехватить любые ошибки, и хотя бы попытаться записать, что пошло не так?
Ли
11
Никогда ... кроме случаев, когда это абсолютно необходимо. Никогда - сильное слово, и всегда есть исключения из правил. Если вы создаете фреймворк, не так уж и маловероятно, что вам придется вылавливать и обрабатывать определенные ошибки, даже если это просто регистрация.
Робин
Как насчет ошибок, например, NoSuchMethodError, которые возникают из-за методов сторонней библиотеки?
ha9u63ar
@OutlawProgrammer Для справки, есть и другие способы провести тот же тест:boolean assertionsEnabled = false; assert assertionsEnabled = true;
shmosel
16

Как правило, вы всегда должны ловить java.lang.Errorи записывать это в журнал или отображать его пользователю. Я работаю в службе поддержки и ежедневно вижу, что программисты не могут сказать, что произошло в программе.

Если у вас есть поток демона, вы должны предотвратить его завершение. В остальных случаях ваше приложение будет работать правильно.

Ловить надо только java.lang.Errorна высшем уровне.

Если вы посмотрите на список ошибок, вы увидите, что с большинством из них можно справиться. Например, ошибка ZipErrorвозникает при чтении поврежденных zip-файлов.

Наиболее частыми ошибками являются OutOfMemoryErrorи NoClassDefFoundError, которые в большинстве случаев являются проблемами во время выполнения.

Например:

int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];

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

NoClassDefFoundErrorчаще всего возникают, если библиотеки нет или вы работаете с другой версией Java. Если это необязательная часть вашей программы, вам не следует прекращать ее.

Я могу привести еще много примеров того, почему лучше поймать Throwableна верхнем уровне и создать полезное сообщение об ошибке.

Horcrux7
источник
Я подозреваю, что в таких случаях лучше отказать демону с соответствующими предупреждениями, чем иметь возможность, что он останется живым как некий эфирный призрак на неудачной jvm, где он может дать ложный предлог, что он действительно жив и что-то делает
Эндрю Norman
OutOfMemoryErrorне является ошибкой во время выполнения, нет гарантии, что приложение сможет исправить ее. Если вам повезет, вы можете получить OOM, new byte[largeNumber]но если этого выделения было недостаточно, чтобы вызвать OOM, оно могло быть запущено в следующей строке или следующем потоке. Это проблема времени выполнения, потому что если lengthэто ненадежный ввод, он должен быть проверен перед вызовом new byte[].
Jeeyoung Kim
NoClassDefFoundErrorможет произойти где угодно , так как он вызывается, когда скомпилированный код Java не может найти класс. Если ваш JDK настроен неправильно, он может сработать при попытке использовать java.util.*класс, и практически невозможно программировать против него. Если вы дополнительно включаете зависимость, вы должны использовать ее, ClassLoaderчтобы проверить, существует ли она, что вызывает ClassNotFoundException.
Jeeyoung Kim
1
ZipErrorуказывает, что файл jar, содержащий классы, является поврежденным файлом zip. Это довольно серьезная проблема, и на данный момент вы не можете доверять никакому исполняемому коду, и было бы безответственно пытаться «оправиться» от него.
Jeeyoung Kim
2
В общем, может быть полезно поймать java.lang.Errorили java.lang.Throwableна верхнем уровне и попытаться что-то с ним сделать - например, записать сообщение об ошибке. Но на этом этапе нет гарантии, что это будет выполнено. Если ваша JVM работает с OOM, попытка регистрации может выделить больше Strings, что вызовет другой OOM.
Jeeyoung Kim
15

В многопоточной среде его чаще всего хочется поймать! Когда вы поймаете это, зарегистрируйте его и завершите работу всего приложения! Если вы этого не сделаете, какой-то поток, который может выполнять какую-то важную часть, будет мертв, а остальная часть приложения будет думать, что все в порядке. Из-за этого может случиться много нежелательных ситуаций. Одна из самых маленьких проблем заключается в том, что вы не сможете легко найти корень проблемы, если другие потоки начнут генерировать некоторые исключения из-за того, что один поток не работает.

Например, обычно цикл должен быть:

try {
   while (shouldRun()) {
       doSomething();
   }
}
catch (Throwable t) {
   log(t);
   stop();
   System.exit(1);
}

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

Sarmun
источник
1
Поймать OutOfMemoryError и продолжить, вместо того, чтобы сразу же существовать, неразумно, потому что ваша программа тогда находится в неопределенном состоянии .
Raedwald
1
вызывающая система, выход при перехвате throwable имеет непреднамеренное последствие уничтожения всего, что работает на JVM, а не только рассматриваемого приложения. Обычно это не является хорошей практикой для веб-приложений, которые могут размещаться на сервере приложений с другими веб-приложениями (не говоря уже о том, что это повлияет на сам сервер приложений).
Эндрю Норман
9

Очень редко.

Я бы сказал только на верхнем уровне потока, чтобы ПОПЫТАТЬСЯ выдать сообщение с причиной смерти потока.

Если вы работаете во фреймворке, который делает это за вас, оставьте это фреймворку.

Darron
источник
6

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

nicerobot
источник
6

ErrorОбычно не должны быть пойманы , поскольку это указывает на ненормальное состояние , которое никогда не должно произойти .

Из спецификации Java API для Errorкласса:

Это Errorподкласс, Throwable который указывает на серьезные проблемы, которые разумное приложение не должно пытаться уловить. Большинство таких ошибок являются ненормальными. [...]

От метода не требуется объявлять в своем предложении throws какие-либо подклассы Error, которые могут быть сгенерированы во время выполнения метода, но не обнаружены, поскольку эти ошибки являются ненормальными условиями, которые никогда не должны возникать.

Как упоминается в спецификации, выдается Errorтолько в тех случаях, когда есть вероятность, что когда возникает Errorошибка, приложение может сделать очень мало, а в некоторых случаях сама виртуальная машина Java может находиться в нестабильном состоянии (например, VirtualMachineError)

Хотя an Errorявляется подклассом, Throwableчто означает, что он может быть перехвачен try-catchпредложением, но, вероятно, в этом нет необходимости, поскольку приложение будет в ненормальном состоянии, когда ErrorJVM сгенерирует его.

Там также краткий раздел по этой теме в разделе 11.5 Иерархия исключений из спецификации языка Java, 2nd Edition .

coobird
источник
6

Если вы достаточно сумасшедшие, чтобы создавать новую среду модульного тестирования, вашему исполнителю тестов, вероятно, потребуется отловить java.lang.AssertionError, выдаваемый любыми тестовыми примерами.

В противном случае смотрите другие ответы.

noahlz
источник
5

И есть пара других случаев, когда, если вы поймаете ошибку, вам придется ее повторно выбросить . Например, ThreadDeath никогда не следует ловить, это может вызвать большую проблему, если вы поймаете его в изолированной среде (например, на сервере приложений):

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

Гийом
источник
2
На самом деле это не проблема, потому что вы просто не улавливаете Errors.
Bombe,
4

Очень-очень редко.

Я сделал это только для одного очень известного случая. Например, java.lang.UnsatisfiedLinkError может быть сгенерирован, если два независимых ClassLoader загружают одну и ту же DLL. (Я согласен с тем, что мне следует переместить JAR в общий загрузчик классов)

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

Даже программист на C / C ++ выдает сообщение об ошибке и сообщает что-то, чего люди не понимают, прежде чем она завершится (например, сбой памяти).

Деннис С
источник
3

В приложении для Android я обнаруживаю ошибку java.lang.VerifyError . Библиотека, которую я использую, не будет работать на устройствах со старой версией ОС, и код библиотеки выдаст такую ​​ошибку. Конечно, я мог бы избежать ошибки, проверив версию ОС во время выполнения, но:

  • Самый старый поддерживаемый SDK может измениться в будущем для конкретной библиотеки.
  • Блок ошибки try-catch является частью более крупного механизма отката. Некоторые конкретные устройства, хотя они должны поддерживать библиотеку, выдают исключения. Я ловлю VerifyError и все исключения, чтобы использовать запасное решение.
kgiannakakis
источник
3

довольно удобно поймать java.lang.AssertionError в тестовой среде ...

Jono
источник
Какую ценность вы пытаетесь добавить?
lpapp
добавление значения набора тестов без прерывания
Джоно
2

В идеале мы не должны обрабатывать / отлавливать ошибки. Но могут быть случаи, когда нам нужно это сделать, исходя из требований фреймворка или приложения. Скажем, у меня есть демон XML Parser, который реализует DOM Parser, который потребляет больше памяти. Если есть такое требование, как поток парсера , он не должен завершаться , когда он получает OutOfMemoryError , вместо этого он должен обработать его и отправить сообщение / письмо администратору приложения / фреймворка.

user3510364
источник
1

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

Вивек
источник
1

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

Ханох
источник
1

Возникает ошибка, когда JVM больше не работает должным образом или находится на грани. Если вы поймаете ошибку, нет гарантии, что блок catch будет работать, и тем более, что он будет работать до конца.

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

Вы также понизите читабельность вашего кода.

Николас Зозол
источник