Выключение крюка против метода финализатора

Я просто не понимаю, почему нужно использовать Runtime.addShutdownHook. Если вы хотите выполнить некоторую очистку при выходе jvm, почему бы не просто перегрузить метод finalize класса daemon. В чем преимущество использования метода завершения выключения при завершении метода.

Также существует устаревшая функция runFinalizersOnExit. Если я установлю его на false, я считаю, что финализаторы не будут работать. Это противоречит гарантии java, что финализаторы всегда запускаются перед сборками мусора.

Ответ 1

Нет гарантии, что финализатор будет когда-либо. finalize() вызывается, когда объект собирает мусор. Но сборщик мусора не может ничего собирать, когда программа работает.

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

ИЗМЕНИТЬ Я посмотрел на гранивые случаи, когда крюк остановки не выполнен.

Завершение выключения выполняется:

  • Когда все потоки JVM завершили выполнение
  • Из-за вызова System.exit()
  • Поскольку пользователь нажимает CNTRL-C
  • Выключение системного уровня или выход из системы.

Крюк отключения не выполняется:

  • Если VM сбой из-за ошибки в собственном коде, то не может быть никаких гарантий относительно того, будут ли выполняться крючки.
  • Если JVM убивается с помощью команды -kill в Linux или Terminate Process на окнах, JVM мгновенно выходит

Ответ 2

Относительно вашего запроса

Если вы хотите выполнить некоторую очистку при выходе jvm, почему бы просто не перегрузить метод finalize класса демона

Я нашел хорошую информацию из этой статьи

  • finalize() вызывается до того, как сборщик мусора вернет объект. JVM не гарантирует, когда этот метод будет вызван.

  • finalize() вызывается только один раз по потоку GC, если объект восстанавливается из метода finalize, а finalize больше не будет вызываться.

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

  • Любое исключение генерируется методом finalize игнорируется потоком GC

  • System.runFinalization(true) и Runtime.getRuntime().runFinalization(true) методы увеличивают вероятность вызова метода finalize(), но теперь эти два метода устарели. Эти методы очень опасны из-за отсутствия безопасности потока и возможного создания взаимоблокировки.

Возвращаясь к shutdownHooks, в соответствии с оракулом документация

public void addShutdownHook (Thread hook) Регистрирует новый виджет завершения виртуальной машины.

Виртуальная машина Java отключается в ответ на два вида событий:

  • Программа завершается нормально, когда последний поток не-daemon выходит или когда вызывается метод exit (эквивалентно, System.exit) или
  • Виртуальная машина завершается в ответ на прерывание пользователя, например, набирает ^ C или общесистемное событие, такое как выход пользователя из системы или выключение системы.
  • Когда виртуальная машина начнет свою последовательность выключения, она запустит все зарегистрированные крючки отключения в некотором неуказанном порядке и позволит им запускать одновременно. Когда все крючки закончены, он будет запускать все неинвинированные финализаторы, если финализация на выходе будет включена.
  • Наконец, виртуальная машина остановится. Обратите внимание, что потоки демона будут продолжать работать во время последовательности выключения, а также потоки не-демона, если выключение было инициировано вызовом метода exit.

Но даже документация оракула указывала, что

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

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

Учитывая недостатки обоих этих подходов, вы должны следовать ниже подхода

  • Не зависеть от finalize() или shutdown hooks, чтобы выпустить критические ресурсы в вашем приложении.

  • использовать try{} catch{} finally{} блокировать соответствующим образом и выпускать критические ресурсы в блоке finally(}. Во время освобождения ресурсов в блоке finally{} catch Exception и Throwable.