Зачем вам реализовывать finalize ()? - PullRequest
355 голосов
/ 01 октября 2008

Я перечитал множество вопросов о новичке по Java на finalize() и считаю, что это немного удивляет, что никто не дал понять, что finalize () - ненадежный способ очистки ресурсов. Я видел, как кто-то комментирует, что он использует его для очистки соединений, что действительно страшно, поскольку единственный способ приблизиться к гарантии закрытия соединения - это реализовать try (catch) в конце концов.

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

Итак, мой вопрос: какие существуют варианты использования для реализации finalize(), которые не могут быть обработаны более надежно с помощью другого процесса или синтаксиса в языке?

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

Ответы [ 21 ]

216 голосов
/ 01 октября 2008

Вы можете использовать его в качестве backstop для объекта, содержащего внешний ресурс (сокет, файл и т. Д.). Реализуйте метод close() и документ, который нужно вызвать.

Реализуйте finalize(), чтобы выполнить обработку close(), если обнаружите, что она не была выполнена. Может быть, с чем-то, сброшенным в stderr, чтобы указать, что вы убираете после вызывающего ошибки глагола.

Обеспечивает дополнительную безопасность в исключительной / глючной ситуации. Не каждый абонент будет делать правильные вещи try {} finally {} каждый раз. К сожалению, но в большинстве случаев это так.

Я согласен, что это редко нужно. И, как отмечают комментаторы, это идет с накладными расходами GC. Используйте только в том случае, если вам нужна безопасность в виде «ремня и подтяжек» в приложении для длительного использования.

Я вижу, что с Java 9 Object.finalize() устарело! Они указывают на java.lang.ref.Cleaner и java.lang.ref.PhantomReference в качестве альтернативы.

163 голосов
/ 01 октября 2008

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

Делать что-либо существенное в финализаторах (в основном все, кроме логирования) также хорошо в трех ситуациях:

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

Если вы считаете, что вам нужен finalize (), иногда вам действительно нужна фантомная ссылка 1015 * (которая в приведенном примере может содержать жесткую ссылку на соединение, используемое его референтом, и закрывать его после фантомная ссылка была поставлена ​​в очередь). У него также есть свойство, которое он может таинственно никогда не запускать, но, по крайней мере, он не может вызывать методы или воскрешать завершенные объекты. Так что это как раз подходит для ситуаций, когда вам совершенно не нужно точно закрывать это соединение, но вы бы очень этого хотели, и клиенты вашего класса не могут или не будут звонить близко друг к другу (что на самом деле достаточно справедливо - какой смысл вообще иметь сборщик мусора, если вы разрабатываете интерфейсы, для которых требуется определенное действие, которое нужно выполнить перед сборкой? Это просто возвращает нас во времена malloc / free.)

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

Отказ от ответственности: я работал над реализацией JVM в прошлом. Я ненавижу финализаторы.

54 голосов
/ 01 октября 2008

Простое правило: никогда не используйте финализаторы.

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

Из статьи Брайана Гетца:

Объекты с финализаторами (те, которые есть нетривиальный метод finalize ()) имеют значительные накладные расходы по сравнению с объекты без финализаторов, и должны быть использованы экономно. финализируемые объекты медленнее распределять и медленнее собирать. При распределении время JVM должна зарегистрировать любой финализируемые объекты с мусором коллектор, и (по крайней мере, в Внедрение HotSpot JVM) финализируемые объекты должны следовать более медленный путь выделения, чем у большинства других объекты. Точно так же, финализируемый предметы тоже собирают медленнее. Это занимает минимум две сборки мусора циклы (в лучшем случае) перед финализируемый объект может быть исправлен, и сборщик мусора должен делать дополнительная работа для вызова финализатора. Результат - больше времени распределение и сбор предметов и больше давления на мусор коллектор, потому что память используется недоступные завершаемые объекты сохраняется дольше. Объедините это с тот факт, что финализаторы не являются гарантированно работать в любом предсказуемом сроки, или даже вообще, и вы можете увидеть, что есть относительно немного ситуации, для которых финализация правильный инструмент для использования.

43 голосов
/ 01 октября 2008

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

33 голосов
/ 01 октября 2008

Я профессионально занимаюсь Java с 1998 года и никогда не внедрял finalize(). Не один раз.

26 голосов
/ 01 октября 2008

Принятый ответ хорош, я просто хотел добавить, что теперь есть способ завершить работу, даже не используя ее вообще.

Посмотрите на "Справочные" классы. Слабая ссылка, фантомная ссылка и мягкая ссылка.

Вы можете использовать их, чтобы сохранить ссылку на все ваши объекты, но эта ссылка ОДНА раз не остановит GC. Неплохо то, что вы можете вызывать метод, когда он будет удален, и этот метод может быть гарантированно для вызова.

Что касается завершения: Я использовал finalize один раз, чтобы понять, какие объекты были освобождены. Вы можете играть в некоторые аккуратные игры со статикой, подсчетом ссылок и тому подобным - но это было только для анализа, но следите за кодом, подобным этому (не только в финализации, но именно там вы, скорее всего, его увидите):

public void finalize() {
  ref1 = null;
  ref2 = null;
  othercrap = null;
}

Это признак того, что кто-то не знал, что делал. Такая «очистка» практически никогда не нужна. Когда класс GC'd, это делается автоматически.

Если вы найдете такой код в финальной версии, это гарантирует, что тот, кто его написал, был в замешательстве.

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

Это никогда не должно использоваться, чтобы убрать вещи "Быстрее".

25 голосов
/ 05 октября 2008

Я не уверен, что вы можете сделать из этого, но ...

itsadok@laptop ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

Итак, я думаю, что Солнце обнаружило несколько случаев, когда (по их мнению) его следует использовать.

21 голосов
/ 02 июня 2009
class MyObject {
    Test main;

    public MyObject(Test t) {    
        main = t; 
    }

    protected void finalize() {
        main.ref = this; // let instance become reachable again
        System.out.println("This is finalize"); //test finalize run only once
    }
}

class Test {
    MyObject ref;

    public static void main(String[] args) {
        Test test = new Test();
        test.ref = new MyObject(test);
        test.ref = null; //MyObject become unreachable,finalize will be invoked
        System.gc(); 
        if (test.ref != null) System.out.println("MyObject still alive!");  
    }
}

====================================

результат:

This is finalize

MyObject still alive!

=====================================

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

9 голосов
/ 13 февраля 2009

finalize() может быть полезно для обнаружения утечек ресурсов. Если ресурс должен быть закрыт, но это не так, напишите, что он не был закрыт для файла журнала, и закройте его. Таким образом вы устраните утечку ресурсов и дадите себе возможность узнать, что это произошло, чтобы вы могли ее исправить.

Я программирую на Java с версии 1.0 alpha 3 (1995), и мне еще предстоит переопределить finalize для чего-либо ...

6 голосов
/ 01 октября 2008

Вы не должны зависеть от finalize () для очистки ваших ресурсов. finalize () не будет работать, пока класс не будет собран мусором, если тогда. Гораздо лучше явно освобождать ресурсы, когда вы их используете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...