Очистить код в finalize () или finally ()? - PullRequest
9 голосов
/ 04 декабря 2009

У меня было общее мнение, что очистка ресурсов выполняется в блоке finally,
недавно я нашел этот фрагмент кода в классе, и он переопределял метод Object class 'finalize().

protected void finalize() {  
    try {
        In.close(); 
        Out.close();
        socket.close();
    }
    catch (Exception e) {
        //logger code here
    }
}

Это хорошая идея? Каковы плюсы и минусы finalize() над finally?

Ответы [ 9 ]

16 голосов
/ 04 декабря 2009

Блок finally - это просто блок кода, который всегда выполняется после блока try, даже если есть исключение. то есть оно локально по объему

Метод finalize() - это подход для очистки всего объекта при его сборке мусора.

Документация по Java для finalize ()

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

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

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

8 голосов
/ 04 декабря 2009

Всегда убирайте вещи в конце концов.

Очистка в финале не гарантируется.

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

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

5 голосов
/ 04 декабря 2009

Phantom References будет делать то, что вы хотите.

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

Существует очень недооцененный тип объекта, который называется "Ссылки". Один сделан явно для вещей, для которых, как вы думаете, вы бы использовали finalize.

" Призрачная ссылка объектов, которые ставятся в очередь после того, как сборщик определит, что их референты могут быть возвращены в противном случае."

Мне только что пришло в голову, что ДОЛЖНО быть описание этого в сети - так что я заменю все "инструкции", которые я только что написал, этой ссылкой.

4 голосов
/ 04 декабря 2009

Они не связаны. Это все равно что спросить: «Должны ли вы создавать объекты в инициализаторе или обычными методами?» Мол, это зависит от того, что вы делаете с объектами. Финализатор очищает состояние объекта во время его уничтожения (возможно, это не то, на что вы должны полагаться), в то время как блок finally выполняет код после блока try. Не существует какой-либо распространенной ситуации, когда вы могли бы выбирать одно или другое, поскольку они делают разные вещи.

2 голосов
/ 04 декабря 2009

Наконец-то. Финализация плохая в том смысле, что она никогда не может быть вызвана. Используйте финализировать только как страховочную сетку. Например, у InputStream должен быть финализатор, который закрывает поток, в котором находятся applcicationforgets. Однако приложение должно закрыть его.

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

2 голосов
/ 04 декабря 2009

Finalize, вероятно, плохая идея, если ваше приложение вызывает создание множества этих объектов. Это связано с тем, что finalize вызовет узкое место, поскольку объекты станут пригодными для сбора мусора.

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

1 голос
/ 04 декабря 2009

Существует несколько проблем с кодом в вопросе, в том числе:

  • Большая проблема: похоже, вы пытаетесь закрыть сокет. Даже если вы не закроете его должным образом, он закроется в своем собственном финализаторе. Добавление еще одного финализатора больше не делает его закрытым.
  • Исключение, выданное первым close, не позволит другим выполнить (как это происходит, это не имеет значения в этом примере из-за особого поведения Socket).
  • Если вы переопределите finalize, оставьте значение Throwable (и добавьте @Override). Технически вы должны также вызвать супер в блоке finally.
  • Модель памяти Java очень странна, когда дело доходит до финализаторов (предыдущее выполнение кода не обязательно происходит до выполнения финализатора). Я бы объяснил проблему, но вам нужно знать, что вам нужно держаться подальше от финалистов.

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

0 голосов
/ 02 сентября 2013

Джошуа Блох дает очень четкую рекомендацию в своей книге Эффективная Java (2-е издание) . Скопировано из главы 2 Пункт 7: Избегайте финализаторов :

Финализаторы непредсказуемы, часто опасны и, как правило, не нужны. Их использование может вызвать нестабильное поведение, плохую производительность и проблемы с переносимостью. У финализаторов есть несколько допустимых применений, о которых мы поговорим позже в этом пункте, но, как правило, вам следует избегать финализаторов.

Пожалуйста, прочитайте ссылку, чтобы узнать, почему.

0 голосов
/ 02 сентября 2013

Если вы ищете альтернативу finalize(), правильный вопрос будет:

  • Зачем использовать явный метод close(), как, например, все классы stream и writer / reader в java.io. * и многие другие - когда есть finalize()?

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

Конечно, вызов метода close() (лучше всего делать в блоке finally или в самом методе close()) должен документироваться автором, а затем вспоминаться для вызова теми, кто использует код. Но есть много примеров (не только java.io.*), где это навязывается и работает.

Кстати: close() - это просто соглашение.

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