Почему именно PhantomReference следует предпочесть доработке?

И то, и другое можно использовать для очистки, гарантий почти нет, но PR требует большего кодирования упряжи. Итак, имея два варианта, почему именно я должен предпочесть один другому?

Javadoc 9 описывает финализацию как очень проблематичный, но это не делает его альтернативу автоматически лучше, верно?

Также javadoc описывает PhantomReference как предоставление «более гибких и эффективных способов высвобождения ресурсов, когда объект становится недоступным», но без указания причины. Что ж, думаю, эти ребята знают какие-то секреты, но мне интересно - нельзя ли сделать этот выбор более очевидным?

Разница

Вот все различия между финализацией (FZ) и референсом пантома (PR), которые я обнаружил, пожалуйста, поправьте меня, если я что-то упустил.

  1. Может использоваться для действий по очистке?

    • Yes for both.
  2. Требуется поддерживать новый поток?

    • PR: yes, you must define a queue watcher thread in order to do cleanup ASAP
    • FZ: no
  3. Требуется новый класс для определения?

    • PR: yes, you must extend PhantomReference to act meaningfully
    • FZ: no
  4. Может ли процессор очистки получить доступ к референтному объекту?

    • PR: no
    • ФЗ: да, и это удобно
  5. Надежно ли это работает в моей личной практике?

    • Yes for both.
  6. Может привести к проблемам с производительностью, взаимоблокировкам и зависаниям?

    • Yes for both. Depends on your code, isn't?
  7. Могут ли ошибки в процессоре очистки привести к утечке ресурсов?

    • Yes for both. Depends on your code, isn't?
  8. Можно отменить, если в этом больше нет необходимости?

    • PR: yes
    • ФЗ: нет, если говорить строго, но сразу return это плохо?
  9. Указан ли порядок вызовов между несколькими экземплярами?

    • PR: no info
    • FZ: no - "не указан порядок среди вызовов финализации методов разных объектов" (java.lang.Object)
  10. Вызов гарантирован?

    • PR: no info - you can only "request to be notified of changes in an object's reachability" (java.lang.ref)
    • FZ: нет — «Метод finalize может быть вызван для финализируемого объекта только после неопределенной задержки, если вообще будет вызван» (java.lang.Object)
  11. Есть какие-то гарантии по срокам?

    • PR: no - "Some time after the garbage collector determines that the reachability of the referent has changed to the value corresponding to the type of the reference" (java.lang.ref)
    • FZ: нет — «Язык программирования Java не указывает, как скоро будет вызван финализатор» (JLS), «Метод finalize может быть вызван для финализируемого объекта только после неопределенной задержки, если вообще будет вызван» (java.lang. Объект)
  12. Может ли this воскреснуть во время обработки?

    • PR: no, and that's not bad
    • ФЗ: да, официально поддерживается

Ссылки:


person Pavel Vlasov    schedule 17.08.2019    source источник
comment
Посмотрите этот ответ, чтобы узнать, почему финализация является плохим механизмом и устарела: stackoverflow.com/a/56454348/1441122   -  person Stuart Marks    schedule 23.08.2019


Ответы (1)


Большая часть этого была рассмотрена в разделе Должен ли Java 9 Cleaner быть предпочтительнее финализации? уже. Поскольку Cleaner API основан на PhantomReference, большая часть его применима и к непосредственному использованию PhantomReference.

Короче говоря, вы не должны заменять finalize() использования на PhantomReference или Cleaner. Для ресурсов, не связанных с памятью, вы должны предпочесть явное закрытие сразу после их использования с помощью try-with-resources везде, где это возможно. Взаимодействие со сборщиком мусора может действовать как запасной вариант для обнаружения ошибок программирования, но не должно становиться предпочтительным способом очистки ресурсов.

В связи с этим возможность отказаться от очистки, когда ресурс был правильно закрыт, имеет большое значение, так как это будет нормой. Вы недооцениваете его влияние. Как только ваш класс имеет нетривиальный финализатор, его объектам требуется два цикла сборки мусора, чтобы быть утилизированными, даже если finalize() немедленно возвращается после проверки условия. Это может иметь значение между сбором в второстепенном gc или переходом в старое поколение.

Самым крайним примером может быть кратко используемый ресурс, представленный чисто локальным объектом, выделение памяти которого может быть полностью исключено после применения Escape Analysis, тогда как наличие нетривиального finalize() метода неизменно подразумевает глобальный escape, который предотвращает эту оптимизацию (среди прочего). , например устранение блокировки).

Хотя Cleaner API запускает выделенный поток, при использовании PhantomReference это не требуется. Вы также можете опросить очередь сразу, когда ресурс используется или собирается выделить новый. Это не гарантирует быстрого освобождения ресурса (которого в любом случае не гарантирует очистка, запускаемая сборщиком мусора), но вы гарантируете, что выделение не завершится ошибкой, пока собранный объект без необходимости удерживает ресурсы.

Даже если вы используете выделенные потоки для очистки, существует фундаментальная разница между запуском потоков под вашим контролем и финализаторами, вызываемыми неопределенными потоками JVM вне вашего контроля, когда ошибочный метод finalize() другой библиотеки может заблокировать поток, необходимый для вашей очистки. JVM может одновременно вызывать несколько финализаторов, в то время как вы можете решить, сколько потоков использовать для очистки на основе PhantomReference.

person Holger    schedule 19.08.2019