2008-10-01 4 views
315

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

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

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

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

+0

HI finalize() метод очень хорошо объяснен здесь http://howtodoinjava.com/2012/10/31/why-not-to-use-finalize-method-in-java/ – 2014-04-14 09:30:03

ответ

191

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

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

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

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

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

+3

Я предполагаю, что я вообще хорошо документирую, а затем кричу на людей, когда они не читают документацию. Думаю, это тоже помогло бы. * grin * – 2008-10-01 15:36:28

+31

Просто убедитесь, что исключение никогда не вызывается finalize(), или сборщик мусора не будет продолжать очистку этого объекта, и вы получите утечку памяти. – skaffman 2008-10-01 16:04:11

+16

Finalize не гарантируется, что вызывается, поэтому не рассчитывайте на его освобождение ресурса. – flicken 2008-10-01 17:14:33

31

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

+0

Как уничтожить открытый класс объект тогда? Это вызывает проблемы в ADF. В принципе, предполагается перезагрузить страницу (получение свежих данных из API), но она берет старый объект и показывает результаты. – 2016-03-04 13:26:23

+1

@ Начинающий вызов InfantPro'Aravind не удаляет объект. Это метод, который вы реализуете, который JVM может выбрать для вызова, если и когда мусор собирает ваш объект. – 2016-03-04 14:21:46

+0

@PaulTomblin, ohk благодарит за эту деталь. Любое предложение по обновлению страницы в ADF? – 2016-03-04 17:02:11

2

При написании кода, который будет использоваться другими разработчиками, для вызова которого требуется какой-то метод очистки, чтобы высвободить ресурсы. Иногда другие разработчики забывают вызвать метод очистки (или закрыть, или уничтожить или что-то еще). Чтобы избежать возможных утечек ресурсов, вы можете проверить метод finalize, чтобы убедиться, что метод был вызван, и если бы вы не могли его назвать.

Многие драйверы баз данных делают это в своих реализациях Statement и Connection, чтобы обеспечить небольшую безопасность для разработчиков, которые забыли закрыть их.

6

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

43

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

1

Будьте осторожны, что вы делаете в финализации().Особенно, если вы используете его для таких вещей, как call close(), чтобы обеспечить очистку ресурсов. Мы столкнулись с несколькими ситуациями, когда у нас были JNI-библиотеки, связанные с запущенным Java-кодом, и при любых обстоятельствах, когда мы использовали finalize() для вызова методов JNI, мы получили бы очень плохое повреждение кучи java. Коррупция не была вызвана основным кодом JNI, все трассировки памяти были хорошими в родных библиотеках. Дело в том, что мы вообще вызывали методы JNI из finalize().

Это было с JDK 1.5, который все еще широко используется.

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

145

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

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

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

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

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

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

2

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

24

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

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

Посмотрите на классы «Reference». Слабая ссылка и т. Д.

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

Еще одно, на что обратить внимание. Каждый раз, когда вы видите что-то вроде этого нигде в коде (не только в финализации, но вот где вы, скорее всего, чтобы увидеть его):

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

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

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

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

Его никогда не следует использовать для очистки вещей «быстрее».

49

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

С article Брайаном Гетца:

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

5

Чтобы выделить пункт в приведенных выше ответах: финализаторы будут выполняться на одиночной резьбе GC. Я слышал о большой демоверсии Sun, где разработчики добавили небольшой сон некоторым финализаторам и намеренно привнесли на свои колени 3D-демонстрацию 3D.

Лучше всего избегать, за исключением, возможно, диагностики test-env.

Eckel's Thinking in Java имеет a good section на этом.

24

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

[email protected] ~/jdk1.6.0_02/src/ 
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l 
41 

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

8

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

Я программирую в Java, так как 1.0 альфа 3 (1995), и я до сих пор не переопределяют завершить ни за что ...

20
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!"); 
    } 
} 

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

результат:

Это окончательно

MyObject еще жив!

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

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

2

Редактировать: Хорошо, это действительно не работает. Я реализовал это и думал, что если это не удается, это нормально для меня, но он даже не вызвал метод finalize за один раз.

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

@Override 
public void finalize() 
{ 
    try {saveCache();} catch (Exception e) {e.printStackTrace();} 
} 

public void saveCache() throws FileNotFoundException, IOException 
{ 
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp")); 
    out.writeObject(cache); 
} 
2

Это может быть удобно, чтобы удалить вещи, которые были добавлены к глобальному/статическому месту (по необходимости), и должно быть удален, когда объект удаляется.Например:

 
    private void addGlobalClickListener() { 
     weakAwtEventListener = new WeakAWTEventListener(this); 

     Toolkit.getDefaultToolkit().addAWTEventListener(weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK); 
    } 

    @Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 

     if(weakAwtEventListener != null) { 
      Toolkit.getDefaultToolkit().removeAWTEventListener(weakAwtEventListener); 
     } 
    } 
0

IIRC - вы можете использовать метод ФИНАЛИЗ как средство реализации механизма объединения средств для дорогих ресурсов - поэтому они не получают GC слишком.

0

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

Однако this answer показывает, что, по крайней мере, в java8 с JIT-компилятором вы сталкиваетесь с неожиданными проблемами, когда иногда вызывается финализатор даже до того, как вы закончите чтение из потока, поддерживаемого вашим объектом.

Так что даже в этой ситуации вызывать завершение не рекомендуется.

0

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

public void finalize() throws Throwable { 
    super.finalize(); 
    if (destructiveFinalize) { 
     T item; 
     for (int i = 0, l = length(); i < l; i++) { 
      item = get(i); 
      if (item == null) { 
       continue; 
      } 
      if (item instanceof Window) { 
       ((Window) get(i)).dispose(); 
      } 
      if (item instanceof CompleteObject) { 
       ((CompleteObject) get(i)).finalize(); 
      } 
      set(i, null); 
     } 
    } 
} 

(CompleteObject является интерфейс я сделал, что позволяет определить, что вы реализовали редко реализованы Object методы как #finalize(), #hashCode() и #clone())

Таким образом, с помощью сестры #setDestructivelyFinalizes(boolean), программа, использующая мою коллекцию, может (помочь) гарантировать, что уничтожение ссылки на эту коллекцию также разрушает ссылки на ее содержимое и предоставляет любые окна, которые могут привести к непредвиденному сбою JVM. Я также рассматривал возможность остановки любых потоков, но это открыло совершенно новую банку червей.

0

Ресурсы (файл, сокет, поток и т. Д.) Должны быть закрыты, как только мы закончим с их использованием. Как правило, они имеют метод close(), который мы обычно вызываем в finally разделе try-catch операторов. Иногда finalize() также может использоваться несколькими разработчиками, но IMO, что не подходит, так как нет никакой гарантии, что finalize будет вызываться всегда.

В Java 7 мы получили try-with-resources заявление, которое может быть использовано как:

try (BufferedReader br = new BufferedReader(new FileReader(path))) { 
    // Processing and other logic here. 
} catch (Exception e) { 
    // log exception 
} finally { 
    // Just in case we need to do some stuff here. 
} 

В приведенном выше примере примерочных с-ресурс будет автоматически закрывать ресурс BufferedReader путем вызова метода close(). Если мы хотим, мы также можем реализовать Closeable в наших собственных классах и использовать его аналогичным образом. ИМО кажется более опрятным и понятным.