В Java использование throw / catch как части логики, когда на самом деле нет ошибки, как правило, является плохой идеей (частично), потому что выбрасывать и перехватывать исключение дорого, и делать это много раз в цикле обычно гораздо медленнее, чем другие. управляющие структуры, которые не включают в себя исключения.
Мой вопрос заключается в том, являются ли затраты, понесенные в самом броске / захвате, или при создании объекта Exception (поскольку он получает много информации времени выполнения, включая стек выполнения)?
Другими словами, если я делаю
Exception e = new Exception();
но не бросайте его, это большая часть стоимости броска, или обработка броска + улова стоит дорого?
Я не спрашиваю, добавляет ли код в блок try / catch стоимость выполнения этого кода, я спрашиваю, является ли перехват Exception дорогой частью или создает (вызывает конструктор) Exception - дорогую часть ,
Еще один способ задать вопрос: если я сделал один экземпляр Exception, а затем бросил и поймал его снова и снова, это было бы значительно быстрее, чем создание нового исключения каждый раз, когда я выбрасывал?
источник
Ответы:
Создание объекта исключения не дороже, чем создание других обычных объектов. Основная стоимость скрыта в нативном
fillInStackTrace
методе, который проходит через стек вызовов и собирает всю необходимую информацию для построения трассировки стека: классы, имена методов, номера строк и т. Д.Миф о высокой стоимости исключений проистекает из того факта, что большинство
Throwable
конструкторов неявно вызываютfillInStackTrace
. Однако есть один конструктор для созданияThrowable
без трассировки стека. Это позволяет создавать броски, которые очень быстро создаются. Другой способ создать легкие исключения - переопределитьfillInStackTrace
.Теперь насчет бросать исключение?
На самом деле, это зависит от того, где заброшенного исключения поймано .
Если он перехватывается одним и тем же методом (или, точнее, в одном и том же контексте, поскольку контекст может включать несколько методов из-за встраивания), он
throw
будет таким же быстрым и простым, какgoto
(конечно, после JIT-компиляции).Однако, если
catch
блок находится где-то глубже в стеке, JVM необходимо развернуть кадры стека, а это может занять значительно больше времени. Это занимает еще больше времени, еслиsynchronized
задействованы блоки или методы, потому что разматывание подразумевает освобождение мониторов, принадлежащих удаленным кадрам стека.Я мог бы подтвердить вышеприведенные утверждения с помощью надлежащих тестов, но, к счастью, мне не нужно этого делать, поскольку все аспекты уже отлично освещены в посте инженера по производительности HotSpot Алексея Шипилева: Исключительная производительность из Lil 'Exception .
источник
Первой операцией в большинстве
Throwable
конструкторов является заполнение трассировки стека, в которой находится большая часть расходов.Тем не менее, существует защищенный конструктор с флагом для отключения трассировки стека. Этот конструктор доступен и при расширении
Exception
. Если вы создаете пользовательский тип исключения, вы можете избежать создания трассировки стека и повысить производительность за счет меньшего количества информации.Если вы создаете отдельное исключение любого типа обычными средствами, вы можете перебрасывать его много раз без дополнительных затрат на заполнение трассировки стека. Тем не менее, его трассировка стека будет отражать, где он был создан, а не где он был брошен в конкретном случае.
Текущие версии Java делают некоторые попытки оптимизировать создание трассировки стека. Нативный код вызывается для заполнения трассировки стека, которая записывает трассу в более легкой, нативной структуре. Соответствующие Java
StackTraceElement
объекты лениво создается из этой записи только тогда , когдаgetStackTrace()
,printStackTrace()
или другие методы , которые предписывают след называются.Если вы исключите генерацию трассировки стека, другой основной ценой будет раскручивание стека между броском и уловом. Чем меньше промежуточных кадров будет найдено до того, как будет обнаружено исключение, тем быстрее это будет.
Разработайте свою программу так, чтобы исключения генерировались только в действительно исключительных случаях, и подобные оптимизации трудно оправдать.
источник
Theres хорошая запись об исключениях здесь.
http://shipilev.net/blog/2014/exceptional-performance/
Вывод заключается в том, что конструкция трассировки стека и разматывание стека являются дорогостоящими деталями. Приведенный ниже код использует функцию,
1.7
позволяющую включать и выключать трассировку стека. Затем мы можем использовать это, чтобы увидеть, какие затраты имеют различные сценарииНиже приведены сроки только для создания объекта. Я добавил
String
сюда, чтобы вы могли видеть, что без написания стека практически нет разницы в созданииJavaException
Object и aString
. С включенной записью стека разница значительна, т.е. как минимум на порядок медленнее.Ниже показано, сколько времени понадобилось, чтобы вернуться из броска на определенную глубину миллион раз.
Следующее почти наверняка является чрезмерным упрощением ...
Если мы берем глубину 16 с записью стека, то создание объекта занимает примерно ~ 40% времени, фактическая трассировка стека составляет подавляющее большинство этого. ~ 93% создания объекта JavaException происходит из-за выполняемой трассировки стека. Это означает, что раскрутка стека в этом случае занимает остальные 50% времени.
Когда мы отключаем создание объекта трассировки стека, приходится гораздо меньшая доля, т.е. 20%, а раскрутка стека теперь составляет 80% времени.
В обоих случаях разматывание стека занимает большую часть общего времени.
Кадры стека в этом примере крошечные по сравнению с тем, что вы обычно найдете.
Вы можете посмотреть на байт-код, используя javap
то есть это для метода 4 ...
источник
Создание трассировки
Exception
соnull
стеком занимает примерно столько же времени, сколькоthrow
иtry-catch
блок вместе. Однако заполнение трассировки стека занимает в среднем в 5 раз больше времени .Я создал следующий тест, чтобы продемонстрировать влияние на производительность. Я добавил в
-Djava.compiler=NONE
Run Configuration, чтобы отключить оптимизацию компилятора. Чтобы измерить влияние построения трассировки стека, я расширилException
класс, чтобы воспользоваться конструктором без стека:Контрольный код выглядит следующим образом:
Вывод:
Это подразумевает, что создание a
NoStackException
примерно так же дорого, как и повторение одного и того жеException
. Это также показывает, что созданиеException
и заполнение его трассировки стека занимает примерно в 4 раза больше времени.источник
Эта часть вопроса ...
Кажется, спрашивается, улучшает ли производительность создание исключения и его кэширование где-нибудь. Да, это так. Это то же самое, что отключить запись стека при создании объекта, потому что это уже сделано.
Это время, которое я получил, пожалуйста, прочитайте предостережение после этого ...
Конечно, проблема в том, что ваша трассировка стека теперь указывает на то, где вы создали экземпляр объекта, а не на тот, откуда он был брошен.
источник
Используя ответ @ AustinD в качестве отправной точки, я сделал несколько настроек. Код внизу.
Помимо добавления случая, когда один экземпляр Exception генерируется неоднократно, я также отключил оптимизацию компилятора, чтобы мы могли получить точные результаты производительности. Я добавил
-Djava.compiler=NONE
к аргументам VM согласно этому ответу . (В eclipse отредактируйте Run Configuration → Arguments, чтобы установить этот аргумент VM)Результаты:
Поэтому создание исключения стоит примерно в 5 раз дороже, чем его выбрасывание + отлов. Предполагая, что компилятор не оптимизирует большую часть затрат.
Для сравнения, вот тот же тестовый прогон без отключения оптимизации:
Код:
источник