Почему метод финализации включен в Java?


44

Согласно этому посту , мы никогда не должны полагаться на вызываемый метод finalize. Так почему же Java вообще включила его в язык программирования?

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

Ответы:


41

Согласно «Эффективной Java» Джошуа Блоха (второе издание), есть два сценария, когда finalize()это полезно:

  1. Один из них - действовать как «сеть безопасности» на случай, если владелец объекта забудет вызвать его явный метод завершения. Хотя нет гарантии, что финализатор будет вызван быстро, может быть лучше освободить ресурс поздно, чем никогда, в тех (надеюсь, редких) случаях, когда клиенту не удается вызвать явный метод завершения. Но финализатор должен записать предупреждение, если обнаружит, что ресурс не был прерван

  2. Второе законное использование финализаторов касается объектов с нативными пирами. Собственный узел - это нативный объект, которому нормальный объект делегирует нативные методы. Поскольку нативный узел не является нормальным объектом, сборщик мусора не знает об этом и не может вернуть его, когда его Java-узел исправлен. Финализатор является подходящим средством для выполнения этой задачи, при условии, что у нативного партнера нет критических ресурсов. Если собственный одноранговый узел содержит ресурсы, которые должны быть немедленно завершены, у класса должен быть явный метод завершения, как описано выше. Метод завершения должен делать все, что требуется для освобождения критического ресурса.

Для дальнейшего чтения обратитесь к пункту 7, стр. 27.


4
С # 2 звучит так, будто нативный узел - это собственный ресурс, который требует сети безопасности и должен иметь метод завершения, и поэтому не должен быть отдельной точкой.
Mooing Duck

2
@MooingDuck: # 2 - это отдельная точка, потому что, если нативный узел не содержит критических ресурсов (например, это просто объект в памяти), тогда нет необходимости показывать явный метод завершения. Защитная сеть от # 1 явно о том, чтобы иметь метод завершения.
Джоминал

55

Финализаторы важны для управления нативными ресурсами. Например, вашему объекту может потребоваться выделить WidgetHandle из операционной системы, используя не-Java API. Если вы не выпустите этот WidgetHandle, когда ваш объект GC'd, вы будете пропускать WidgetHandles.

Важно то, что случаи "финализатор никогда не вызывается" разбиваются довольно просто:

  1. Программа быстро закрывается
  2. Объект «живет вечно» при жизни программы
  3. Компьютер выключается / ваш процесс убит ОС / и т.д.

Во всех этих трех случаях у вас либо нет собственной утечки (в силу того факта, что ваша программа больше не работает), либо у вас уже есть несобственная утечка (если вы продолжаете выделять управляемые объекты без их GC'd).

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

Однако для управления собственными ресурсами случаи, когда финализатор не запускается, соответствуют случаям, когда вам все равно, не запускается ли он.


Отличный ответ. Я был как будто даже если бы это можно было назвать .. это все равно должно быть включено. Я был смущен некоторое время с вопросом и связанным на StackOverflow.
InformedA

3
Этого не было в вопросе, но его стоит обсудить в контексте «не полагайтесь на то, что вызывается финализатор»: финализатор может быть вызван, но только после сколь угодно большой задержки после того, как объект становится недоступным. Это имеет значение для «родных ресурсов», которые гораздо более скудны, чем память (оказывается, большинство из них).

26
«Финализаторы важны для управления родными ресурсами». - Нееееееееет, извините. Использование финализаторов для управления нативными ресурсами - ужасная идея (именно поэтому у нас есть автозаполнение и совместная работа). GC заботится только о памяти, а не о любом другом ресурсе. Это означает, что вы можете исчерпать собственные ресурсы, но при этом иметь достаточно памяти, чтобы не запускать GC - мы действительно этого не хотим. Также финализаторы делают GC дороже, что тоже не очень хорошая идея.
Во

12
Я согласен вы все равно должны иметь четкую стратегию нативное управления ресурсами, кроме финализаторов, но если ваш объект действительно выделить их и не освобождает их в финализации, вы открываете себя для нативнога утечки в том случае , когда объект GC'd без явного освобождения ресурса. Другими словами, финализаторы являются важной частью собственной стратегии управления ресурсами, а не решением проблемы в целом.
Райан Кавано

1
@ Похоже, правильнее было бы сказать, что финализатор является запасным вариантом в случае, если контракт объекта не close()выполняется ( никогда не вызывается до того, как он станет недоступным)
трещотка урод

5

Назначение этого метода объясняется в документации API следующим образом:

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

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


Если вас дополнительно интересуют причины, по которым разработчики языка выбрали тот «объект, который безвозвратно отбрасывается» ( сборщик мусора ), выходящий за пределы контроля программиста приложения («мы никогда не должны полагаться»), это было объяснено в ответе на вопрос :

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

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

Более подробное и независимое от языка обсуждение этого предпочтения см. В разделе 9.6 «Автоматическое управление памятью» в OOSC (на самом деле, не только этот раздел, но и целую главу 9 очень стоит прочитать, если вы заинтересованы в подобных вещах). Этот раздел открывается однозначным утверждением:

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

Предыдущее обсуждение должно быть достаточным, чтобы показать, насколько важно иметь такую ​​возможность. По словам Михаэля Швейцера и Ламберта Штретера:

Объектно-ориентированная программа без автоматического управления памятью примерно такая же, как скороварка без предохранительного клапана: рано или поздно она обязательно взорвется!


4

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


3

ПРЕДУПРЕЖДЕНИЕ: Я могу быть устаревшим, но это мое понимание несколько лет назад:

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

И некоторые GC, как известно, явно задерживают или избегают GC'ing объектов, которые имеют финализаторы, в надежде, что это даст лучшую производительность в тестах.

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

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


1

В Руководстве по стилю Google Java есть несколько мудрых советов по этому вопросу:

Это крайне редко, чтобы переопределить Object.finalize.

Совет : не делай этого. Если вам абсолютно необходимо, сначала внимательно прочитайте и поймите пункт 7 Эффективной Java «Избегайте финализаторов», а затем не делайте этого.


5
Почему я восхищаюсь упрощением проблемы, «не делай этого» не является ответом на вопрос «почему она существует».
Пьер Арло

1
Я предполагаю, что я не очень четко изложил это в своем ответе, но (IMO) ответ на вопрос "почему он существует?" это "не должно". В тех редких случаях, когда вам действительно нужна операция завершения, вы, вероятно, захотите создать ее самостоятельно PhantomReferenceи ReferenceQueueвместо нее .
Даниэль Приден

Я имел в виду, хотя * очевидно, хотя я не отрицал тебя. Ответ, получивший наибольшее количество голосов, показывает, насколько полезен этот метод, в некотором смысле лишает вас смысла.
Пьер Арло

1
Даже в родном случае со сверстниками, я думаю PhantomReference, это лучшее решение. Финализаторы - это бородавка, оставшаяся с первых дней Java, и подобные Object.clone()и необработанные типы являются частью языка, о котором лучше всего забыть.
Даниэль Приден

1
Опасности финализаторов описаны в C # более доступным (понятным для разработчиков) способом. Хотя нельзя подразумевать, что базовые механизмы на Java и C # одинаковы; нет ничего в их спецификациях, которые говорят так.
rwong

1

Спецификация языка Java (Java SE 7) гласит:

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.