Почему метод finalize() устарел в Java 9?

(Этот вопрос отличается от Зачем вам когда-либо реализовывать finalize()?. Этот вопрос касается устаревания платформы Java, а другой вопрос касается того, следует ли использовать этот механизм в приложениях.)

Почему метод finalize() устарел в Java 9?

Да, это может быть использовано неправильно (например, сохранить объект от сборки мусора [только один раз] или попытаться закрыть некоторые нативные ресурсы внутри него [хотя это лучше, чем вообще не закрывать]), а также многие другие методы могли быть использованы неправильно.

Так действительно ли finalize() настолько опасен или абсолютно бесполезен, что его необходимо выкинуть из Java?

См. также обсуждения, связанные с ошибкой OpenJDK: bugs.openjdk.java.net/browse/JDK-8165641

Daniel Pryden 15.05.2019 01:15

Короткий ответ, однако: да. Он бесполезен гораздо чаще, чем думают люди, опасен в использовании даже для экспертов, которые очень хорошо в нем разбираются (например, если вы не знаете, что такое ограждение достижимости и как его использовать, я гарантирую, что все ваши финализаторы, управляющие нативными ресурсами, глючит), и это привлекательная неприятность для тех, кто ищет деструкторы в стиле C++. Я говорю: прощайте финализаторы и скатертью дорога.

Daniel Pryden 15.05.2019 01:19

Ваш вопрос может отличаться, но отвечать один и тот же. (Вот почему сообщение сформулировано так: «На этот вопрос уже есть ответ здесь».) Если нет допустимого варианта использования, устаревание является естественным.

Daniel Pryden 15.05.2019 01:44

Вот ветка о том, чем его следует заменить: stackoverflow.com/questions/47762986/replacing-finalize-in-j‌​ava

Thilo 15.05.2019 01:58

Хотя @DanielPryden уже объяснил, почему на самом деле не имеет значения, отличается ли ваш вопрос от дубликата, я бы также добавил, что ваш вопрос, вероятно, не соответствует теме переполнения стека, поскольку на самом деле он не касается проблемы программирования и любых ответов. будет «в основном основанный на мнении». (Я полагаю, вам могло невероятно повезти, и вы получили ответ от человека, ответственного за решение отказаться от поддержки finalize() в JDK 10, но это маловероятно, и любой другой ответ был бы просто предположением.)

skomisa 15.05.2019 05:38

@skomisa Жаль, что этот вопрос был закрыт. Существует ряд вопросов о обоснование для решений, на которые, безусловно, можно ответить. Либо люди, участвующие в решении, могут появиться и ответить на него — что, безусловно, случалось раньше, особенно в отношении недавних решений, — либо ответ может быть где-то записан, либо в сообщении списка рассылки, либо в комментарии в отчете об ошибке. . Обоснование может быть субъективным, но оно может быть окончательным и не основанным исключительно на мнении.

Stuart Marks 04.06.2019 18:09

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

Stuart Marks 04.06.2019 18:10

@StuartMarks: Вы, конечно, правы. Единственное, что я знаю о причинах, это то, что находится в открытом доступе в архивах списков рассылки. Если вы чувствуете, что на вопрос можно дать осмысленный ответ, не превращаясь в повторение того, почему финализаторы проблематичны, у меня нет возражений. Я воздержусь от дальнейших действий по этому вопросу; пожалуйста, проголосуйте за повторное открытие, если хотите. Я признаю, что частью моего стремления закрыть вопрос является несколько разглагольствованная природа формулировки («Так действительно ли метод finalize() настолько опасен или абсолютно бесполезен, чтобы выкинуть его из Java?»).

Daniel Pryden 04.06.2019 19:02

@StuartMarks: Как бы то ни было, поскольку у вас есть золотой значок в теге java, вы можете в одиночку повторно открыть вопрос, если считаете, что он должен быть открытым.

Daniel Pryden 04.06.2019 19:03

@DanielPryden Спасибо за ваши ответы. Un-dupe-молоток работает! Я этого не знал. Я знал о двойном молотке, но я, кажется, помню, что мне приходилось голосовать и ждать других голосов, чтобы снова открыть закрытые вопросы в прошлом. В любом случае я дам ответ в какой-то момент.

Stuart Marks 05.06.2019 05:05

@StuartMarks Что ж, я, безусловно, понимаю вашу (подразумеваемую) точку зрения о том, что, поскольку вы участвовали в принятии решения о прекращении поддержки Object.finalize(), может иметь смысл снова открыть это, как было предложено в другом комментарии. Я предполагаю, что Object.finalize(), возможно, был объявлен устаревшим специально в Java 9 (в отличие от любого более раннего/позднего выпуска) из-за добавления класса Cleaner в Java 9, и JDK-8165641 Устаревший Object.finalize несколько поддерживает эту точку зрения, но это просто предположение с моей стороны. В любом случае, голосование за возобновление работы на основе ваших комментариев.

skomisa 05.06.2019 05:15

@StuartMarks Хех - я вижу, что вопрос был вновь открыт, пока я писал свой комментарий.

skomisa 05.06.2019 05:16

@skomisa Да, Дэниел Прайден указал мне, что я могу сделать это сам, что я и сделал! В любом случае, спасибо за ваш ответ. Я думаю, что есть что сказать, чего нет в другом ответе. Извините, я был немного резок по этому поводу - я действительно думаю, что вопросы об обосновании отличаются от чисто «основанных на мнении» вопросов, таких как «Является ли X лучше, чем Y?» и что можно получить полезные (хотя и субъективные) ответы о логическом обосновании.

Stuart Marks 05.06.2019 05:27

@StuartMarks [1] Я согласен со всем этим, и, оглядываясь назад, я явно поторопился, предположив, что маловероятно, чтобы кто-либо, участвовавший в решении отказаться от поддержки finalize(), ответил здесь - мой плохой. [2] Хотя это и не дубликат, Следует ли предпочесть Java 9 Cleaner финализации? кажется уместным.

skomisa 05.06.2019 05:40

@skomisa (Ах, это основано на закрытом мнении, где мне пришлось ждать повторного открытия голосования, тогда как это было повторно открыто немедленно, потому что оно было закрыто как дубликат. Я перепутал их.) В любом случае, может быть, я тоже снова открыл быстро, потому что этот вопрос теперь, кажется, привлекает плохие ответы!

Stuart Marks 05.06.2019 05:48
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
27
15
12 444
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хотя вопрос был о методе Object.finalize, на самом деле речь идет о механизме завершение в целом. Этот механизм включает в себя не только поверхностный API Object.finalize, но также включает спецификации языка программирования о жизненном цикле объектов и практическое влияние на реализацию сборщиков мусора в JVM.

Много было написано о том, почему финализацию трудно использовать с точки зрения приложения. См. вопросы Зачем вам когда-либо реализовывать finalize()? и Следует ли предпочесть Java 9 Cleaner финализации? и ответы на них. См. также Эффективная Java, 3-е издание Джошуа Блоха, пункт 8.

Вкратце, некоторые моменты о проблемах, связанных с использованием финализаторов:

  • их, как известно, трудно правильно запрограммировать

  • в частности, они могут запускаться неожиданно, когда объект становится недостижимым неожиданно (но правильно); Например, см. мой ответ на этот вопрос

  • финализация может легко нарушить отношения подкласса/суперкласса

  • нет порядка среди финализаторов

  • метод finalize данного объекта вызывается JVM не более одного раза, даже если этот объект «воскрес»

  • отсутствуют гарантии своевременности доработки или даже то, что он будет работать вообще

  • нет явного механизма регистрации или отмены регистрации

Выше перечислены сложности с использованием финализации. Любой, кто рассматривает возможность использования финализации, должен пересмотреть свое решение, учитывая приведенный выше список проблем. Но достаточно ли этих проблем, чтобы отказаться от финализации на платформе Java? Есть несколько дополнительных причин, описанных в разделах ниже.

Завершение потенциально делает системы хрупкими

Даже если вы пишете объект, который правильно использует завершение, это может вызвать проблемы, когда ваш объект интегрируется в более крупную систему. Даже если вы вообще не используете финализацию, интеграция в более крупную систему, некоторые части которой используют финализацию, может привести к проблемам. Общая проблема заключается в том, что рабочие потоки, создающие мусор, должны быть сбалансированы со сборщиком мусора. Если сборщик мусора отстает, по крайней мере, некоторые сборщики могут «остановить мир» и выполнить полную сборку, чтобы наверстать упущенное. Финализация усложняет это взаимодействие. Даже если сборщик мусора успевает за потоками приложения, финализация может создать узкие места и замедлить работу системы или вызвать задержки в освобождении ресурсов, что приведет к исчерпанию этих ресурсов. Это проблема системы. Даже если фактический код, использующий финализацию, правильный, в правильно запрограммированных системах все равно могут возникать проблемы.

(Правка 2021-09-16: этот вопрос описывает проблему, когда система нормально работает при низкой нагрузке, но дает сбой при высокой нагрузке, вероятно, потому, что относительная скорость выделения превышает скорость завершения при высокой нагрузке.)

Завершение способствует проблемам безопасности

SEI CERT Стандарт кодирования Oracle для Java имеет правило MET12-J: не используйте финализаторы. (Обратите внимание, это сайт о безопасном кодировании.) В частности, там говорится

Improper use of finalizers can result in resurrection of garbage-collection-ready objects and result in denial-of-service vulnerabilities.

Oracle Руководство по безопасному кодированию для Java SE более подробно описывает потенциальные проблемы безопасности, которые могут возникнуть при завершении. В этом случае это не проблема с кодом, использующим финализацию. Вместо этого можно использовать финализацию злоумышленником для атаки на чувствительный код, который не защитил себя должным образом. В частности, Руководство 7-3 / ОБЪЕКТ-3 частично утверждает,

Partially initialized instances of a non-final class can be accessed via a finalizer attack. The attacker overrides the protected finalize method in a subclass and attempts to create a new instance of that subclass. This attempt fails ... but the attacker simply ignores any exception and waits for the virtual machine to perform finalization on the partially initialized object. When that occurs the malicious finalize method implementation is invoked, giving the attacker access to this, a reference to the object being finalized. Although the object is only partially initialized, the attacker can still invoke methods on it....

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

Финализация усложняет спецификации

Платформа Java определяется несколькими спецификациями, включая спецификации языка, виртуальной машины и API библиотеки классов. Влияние финализации тонко распространяется на все это, но оно постоянно дает о себе знать. Например, финализация очень тонко взаимодействует с созданием объекта (что уже достаточно сложно). Завершение также появилось в общедоступных API-интерфейсах Java, а это означает, что эволюция этих API-интерфейсов (до сих пор) требовалась, чтобы оставаться совместимыми с ранее указанным поведением. Развитие этих спецификаций удорожается наличием доработки.

Финализация усложняет реализацию

В основном это касается сборщиков мусора. Существует несколько реализаций сборки мусора, и все они должны оплачивать стоимость реализации финализации. Реализации достаточно хороши для минимизации накладных расходов во время выполнения, если финализация не используется. Тем не менее, реализация все еще должна быть там, и она должна быть правильной и хорошо протестированной. Это постоянное бремя разработки и обслуживания.

Резюме

Мы видели в другом месте, что программистам не рекомендуется использовать финализацию. Однако, если что-то бесполезно, это не обязательно означает, что оно должно быть объявлено устаревшим. Приведенные выше пункты иллюстрируют тот факт, что даже если финализация не используется, простое присутствие механизма в платформе налагает постоянные затраты на спецификацию, разработку и обслуживание. Учитывая бесполезность этого механизма и связанные с ним затраты, имеет смысл отказаться от него. В конце концов, избавление от финализации пойдет на пользу всем.

На момент написания этой статьи (04.06.2019) нет конкретного плана по удалению финализации из Java. Тем не менее, это, безусловно, намерение сделать это. Мы объявили метод Object.finalize устаревшим, но не пометили его для удаления. Это формальная рекомендация программистам прекратить использование этого механизма. Неофициально известно, что финализацию использовать нельзя, но формальный шаг сделать, конечно же, необходимо. Кроме того, некоторые методы finalize в библиотечных классах (например, ZipFile.finalize) объявлены устаревшими «для удаления», что означает, что поведение финализации этих классов может быть удалено из будущего выпуска. В конце концов, мы надеемся отключить финализацию в JVM (возможно, сначала опционально, а затем по умолчанию), и в какой-то момент в будущем действительно удалить реализацию финализации из сборщиков мусора.

(Редактировать 2021-11-03: JEP 421 только что был опубликован, в котором предлагается отказаться от финализации для удаления. На момент написания статьи он находится в состоянии «кандидат», но я ожидаю, что он будет продвигаться вперед. Устаревание, добавленное этим JEP, является официальным уведомлением. эта финализация будет удалена в какой-то момент в последующем выпуске Java. Возможно, неудивительно, что материал в этом ответе и в JEP частично совпадает, хотя JEP более точен и описывает умеренную эволюцию нашего мышления по этой теме. .)

Завершение действительно сложное дело. Так что небольшая поправка: только финализатор, пытающийся воскресить себя, срабатывает только один раз. Но финализатор может воскресить другой инкапсулированный объект, достижимый финализатором, после чего создать новый экземпляр этой оболочки, который может снова воскресить объект, процедура, которую можно выполнять произвольное количество раз. Не то чтобы это был рекомендуемый шаблон…

Holger 06.06.2019 14:03

@ Хольгер Верно. Правило касается вызова метода finalize, а не «воскрешения» как таковой. Я обновил текст, чтобы быть более точным.

Stuart Marks 06.06.2019 23:56

@StuartMarks finalization can easily break subclass/superclass relationships Как?

Govinda Sakhare 09.06.2019 12:16

@GovindaSakhare Вкратце, если подклассу по какой-либо причине не удается вызвать super.finalize (), финализация суперкласса не произойдет.

Stuart Marks 09.06.2019 19:51

Другие вопросы по теме