Согласно этому посту , мы никогда не должны полагаться на вызываемый метод finalize. Так почему же Java вообще включила его в язык программирования?
Кажется ужасным решение включить в любой язык программирования функцию, которая может быть вызвана.
Согласно этому посту , мы никогда не должны полагаться на вызываемый метод finalize. Так почему же Java вообще включила его в язык программирования?
Кажется ужасным решение включить в любой язык программирования функцию, которая может быть вызвана.
Ответы:
Согласно «Эффективной Java» Джошуа Блоха (второе издание), есть два сценария, когда finalize()
это полезно:
Один из них - действовать как «сеть безопасности» на случай, если владелец объекта забудет вызвать его явный метод завершения. Хотя нет гарантии, что финализатор будет вызван быстро, может быть лучше освободить ресурс поздно, чем никогда, в тех (надеюсь, редких) случаях, когда клиенту не удается вызвать явный метод завершения. Но финализатор должен записать предупреждение, если обнаружит, что ресурс не был прерван
Второе законное использование финализаторов касается объектов с нативными пирами. Собственный узел - это нативный объект, которому нормальный объект делегирует нативные методы. Поскольку нативный узел не является нормальным объектом, сборщик мусора не знает об этом и не может вернуть его, когда его Java-узел исправлен. Финализатор является подходящим средством для выполнения этой задачи, при условии, что у нативного партнера нет критических ресурсов. Если собственный одноранговый узел содержит ресурсы, которые должны быть немедленно завершены, у класса должен быть явный метод завершения, как описано выше. Метод завершения должен делать все, что требуется для освобождения критического ресурса.
Для дальнейшего чтения обратитесь к пункту 7, стр. 27.
Финализаторы важны для управления нативными ресурсами. Например, вашему объекту может потребоваться выделить WidgetHandle из операционной системы, используя не-Java API. Если вы не выпустите этот WidgetHandle, когда ваш объект GC'd, вы будете пропускать WidgetHandles.
Важно то, что случаи "финализатор никогда не вызывается" разбиваются довольно просто:
Во всех этих трех случаях у вас либо нет собственной утечки (в силу того факта, что ваша программа больше не работает), либо у вас уже есть несобственная утечка (если вы продолжаете выделять управляемые объекты без их GC'd).
Предупреждение «не полагайтесь на вызываемый финализатор» на самом деле означает не использовать финализаторы для логики программы. Например, вы не хотите отслеживать, сколько ваших объектов существует во всех экземплярах вашей программы, увеличивая счетчик в файле где-то во время построения и уменьшая его в финализаторе - потому что нет гарантии, что ваши объекты будут В конечном итоге этот счетчик файлов, вероятно, никогда не вернется к 0. Это действительно особый случай более общего принципа, согласно которому вы не должны зависеть от нормального завершения вашей программы (сбой питания и т. д.).
Однако для управления собственными ресурсами случаи, когда финализатор не запускается, соответствуют случаям, когда вам все равно, не запускается ли он.
close()
выполняется ( никогда не вызывается до того, как он станет недоступным)
Назначение этого метода объясняется в документации API следующим образом:
он вызывается, если и когда виртуальная машина Java определила, что больше нет никаких средств, с помощью которых этот объект мог бы быть доступен любому потоку, который еще не умер, кроме как в результате действия, предпринятого завершением некоторого другого объекта или класс, который готов к завершению ...
обычная цель
finalize
... состоит в том, чтобы выполнить действия по очистке, прежде чем объект будет безвозвратно удален . Например, метод finalize для объекта, который представляет соединение ввода-вывода, может выполнять явные транзакции ввода-вывода, чтобы разорвать соединение до того, как объект будет окончательно отброшен ...
Если вас дополнительно интересуют причины, по которым разработчики языка выбрали тот «объект, который безвозвратно отбрасывается» ( сборщик мусора ), выходящий за пределы контроля программиста приложения («мы никогда не должны полагаться»), это было объяснено в ответе на вопрос :
автоматическая сборка мусора ... устраняет целые классы ошибок программирования, которые мешают программистам на C и C ++. Вы можете разрабатывать Java-код с уверенностью, что система быстро найдет много ошибок и что основные проблемы не будут бездействовать до тех пор, пока ваш рабочий код не будет отправлен.
Выше цитата, в свою очередь, была взята из официальной документации о целях разработки Java , то есть ее можно считать авторитетным справочным материалом, объясняющим, почему разработчики языка Java решили так.
Более подробное и независимое от языка обсуждение этого предпочтения см. В разделе 9.6 «Автоматическое управление памятью» в OOSC (на самом деле, не только этот раздел, но и целую главу 9 очень стоит прочитать, если вы заинтересованы в подобных вещах). Этот раздел открывается однозначным утверждением:
Хорошая ОО-среда должна предлагать механизм автоматического управления памятью, который будет обнаруживать и восстанавливать недоступные объекты, позволяя разработчикам приложений сосредоточиться на своей работе - разработке приложений.
Предыдущее обсуждение должно быть достаточным, чтобы показать, насколько важно иметь такую возможность. По словам Михаэля Швейцера и Ламберта Штретера:
Объектно-ориентированная программа без автоматического управления памятью примерно такая же, как скороварка без предохранительного клапана: рано или поздно она обязательно взорвется!
Финализаторы существуют потому, что от них ожидали, что они станут эффективным средством обеспечения очистки вещей (даже если на практике это не так), а также потому, что когда они были изобретены, это лучшее средство обеспечения очистки (например, фантомные ссылки и пробная версия). -ресурсы) еще не существовало. Оглядываясь назад, Java, вероятно, был бы лучше, если бы усилия, потраченные на реализацию его средства «финализации», были потрачены на другие средства очистки, но это было едва ли понятно во время первоначальной разработки Java.
ПРЕДУПРЕЖДЕНИЕ: Я могу быть устаревшим, но это мое понимание несколько лет назад:
В общем, нет никакой гарантии того, что финализатор запущен - или даже вообще запущен, хотя некоторые JVM позволяют запрашивать полный GC и финализацию до выхода из программы (что, конечно, означает, что программа занимает больше времени) выйти, и который не является режимом работы по умолчанию).
И некоторые GC, как известно, явно задерживают или избегают GC'ing объектов, которые имеют финализаторы, в надежде, что это даст лучшую производительность в тестах.
К сожалению, такое поведение противоречит первоначальным причинам, по которым были рекомендованы финализаторы, и поощряет использование явно вызываемых методов завершения работы.
Если у вас есть объект, который действительно нужно очистить перед тем, как выбросить, и если вы действительно не можете доверять пользователям это, финализатор все же стоит рассмотреть. Но в целом, есть веские причины, по которым вы не видите их в современном Java-коде так часто, как в некоторых ранних примерах.
В Руководстве по стилю Google Java есть несколько мудрых советов по этому вопросу:
Это крайне редко, чтобы переопределить
Object.finalize
.Совет : не делай этого. Если вам абсолютно необходимо, сначала внимательно прочитайте и поймите пункт 7 Эффективной Java «Избегайте финализаторов», а затем не делайте этого.
PhantomReference
и ReferenceQueue
вместо нее .
PhantomReference
, это лучшее решение. Финализаторы - это бородавка, оставшаяся с первых дней Java, и подобные Object.clone()
и необработанные типы являются частью языка, о котором лучше всего забыть.
Спецификация языка Java (Java SE 7) гласит:
Финализаторы дают возможность высвободить ресурсы, которые не могут быть автоматически освобождены автоматическим менеджером хранилища. В таких ситуациях простое восстановление памяти, используемой объектом, не гарантирует, что ресурсы, которые он имел, будут возвращены.