Вы не должны заменять все finalize()
методы на Cleaner
.Тот факт, что устаревание метода finalize()
и введение (a public
) Cleaner
произошло в одной и той же версии Java, указывает только на то, что произошла общая работа над темой, а не на то, что она должна быть заменойдругого.
Другая связанная работа этой версии Java - удаление правила, согласно которому PhantomReference
не очищается автоматически (да, до Java 9 использование PhantomReference
вместо finalize()
все еще).потребовалось два цикла GC для восстановления объекта) и введение Reference.reachabilityFence(…)
.
Первая альтернатива finalize()
- вообще не иметь зависимой от сборки мусора операции.Хорошо, когда вы говорите, что у вас их немного, но я видел полностью устаревшие finalize()
методы в дикой природе.Проблема в том, что finalize()
выглядит как обычный protected
метод, а на некоторых интернет-страницах все еще распространен цепкий миф о том, что finalize()
был своего рода деструктором.Маркировка устарела позволяет сигнализировать разработчику, что это не так, без нарушения совместимости.Использование механизма, требующего явной регистрации, помогает понять, что это не обычный программный поток.И это не повредит, когда выглядит более сложным, чем переопределение одного метода.
Если ваш класс инкапсулирует ресурс без кучи, документация гласит:
Классы, экземпляры которых содержат ресурсы, не относящиеся к куче, должны предоставлять метод, позволяющий явное освобождение этих ресурсов, и они также должны реализовывать AutoCloseable , если это необходимо.
(такэто предпочтительное решение)
Очиститель и PhantomReference предоставляют более гибкие и эффективные способы освобождения ресурсов, когда объект становится недоступным.
Поэтому, когда вам действительно необходимо взаимодействие с сборщиком мусора, даже в этом кратком комментарии к документации указаны две альтернативы, так как PhantomReference
здесь не упоминается как скрытый от разработчика бэкэнд Cleaner
здесь;использование PhantomReference
напрямую является альтернативой Cleaner
, которое может быть еще более сложным в использовании, но также обеспечивает еще больший контроль над временем и потоками, включая возможность очистки в том же потоке, который использовал ресурс.(Сравните с WeakHashMap
, который имеет такую очистку, избегая затрат на многопоточные конструкции).Это также позволяет работать с исключениями, возникающими во время очистки, лучше, чем тихое их проглатывание.
Но даже Cleaner
решает больше проблем, о которых вы знаете.
Существенная проблема,время регистрации.
Объект класса с нетривиальным методом finalize()
регистрируется при выполнении конструктора Object()
.На данный момент объект еще не был инициализирован.Если ваша инициализация завершена с исключением, метод finalize()
все равно будет вызван.Может быть заманчиво решить это с помощью данных объекта, например, установив флаг initialized
на true
, но вы можете сказать это только для ваших собственных данных экземпляра, но не для данных подкласса, который еще не был инициализированкогда ваш конструктор вернется.
Для регистрации очистителя требуется полностью построенный Runnable
, содержащий все необходимые данные для очистки, без ссылки на строящийся объект.Вы можете даже отложить регистрацию, когда распределение ресурсов не произошло в конструкторе (представьте себе несвязанный экземпляр Socket
или Frame
, который не атомарно связан с дисплеем)
A finalize()
метод может быть переопределен без вызова метода суперкласса или безуспешной попытки сделать это в исключительном случае.Предотвращение переопределения метода путем объявления его final
не позволяет подклассам вообще выполнять такие действия по очистке.Напротив, каждый класс может регистрировать чистящие средства без помех для других чистящих средств.
ПредоставленоВы могли бы решить такие проблемы с инкапсулированными объектами, однако, если бы у вас был метод finalize()
для каждого класса, направленного в другое неправильное направление.
Как вы уже обнаружили,Существует метод clean()
, который позволяет немедленно выполнить очистку и удалить очиститель.Поэтому при предоставлении явного метода закрытия или даже реализации AutoClosable
это предпочтительный способ очистки, своевременной утилизации ресурса и устранения всех проблем очистки на основе сборщика мусора.
Обратите внимание, что это согласуется спункты, упомянутые выше.Для объекта может быть несколько очистителей, например, зарегистрированных различными классами в иерархии.Каждый из них может быть запущен индивидуально, с помощью встроенного решения, касающегося прав доступа, только тот, кто зарегистрировал уборщика, получает в руки связанный Cleanable
, чтобы иметь возможность вызывать метод clean()
.
Тем не менее, часто упускают из виду, что худшее, что может случиться при управлении ресурсами с помощью сборщика мусора, это не то, что действие очистки может выполняться позже или вообще не выполняться.Худшее, что может случиться, это то, что он запускается слишком рано .См., Например, finalize (), вызываемый для сильно достижимого объекта в Java 8 .Или действительно хороший, JDK-8145304, Executors.newSingleThreadExecutor (). Submit (runnable) выдает RejectedExecutionException , где финализатор завершает работу службы исполнителя, все еще используемой.
Предоставлено,просто использование Cleaner
или PhantomReference
не решает эту проблему.Но удаление финализаторов и внедрение альтернативного механизма, когда это действительно необходимо, - это возможность тщательно обдумать тему и, возможно, вставить reachabilityFence
s , где это необходимо.Худшее, что вы можете иметь, это метод, который выглядит как простой в использовании, когда на самом деле тема ужасно сложна, и 99% ее использования могут когда-нибудь сломаться.
Далее, в то время какальтернативы являются более сложными, вы сказали сами, они редко нужны.Эта сложность должна влиять только на часть вашей кодовой базы.Любой, почему java.lang.Object
, базовый класс для всех классов, должен содержать метод, обращающийся к редкому угловому случаю программирования Java?