2010-03-24 5 views
248

Мне нужно знать, когда вызывается метод finalize() в JVM. Я создал тестовый класс, который записывает в файл, когда метод finalize() вызывается путем его переопределения. Он не выполнен. Может ли кто-нибудь сказать мне причину, почему он не выполняется?Когда метод finalize() вызывается в Java?

+8

Так же, как примечание стороны: финализации помечен как устаревший в Java 9 – Reg

+0

посмотрите: https://www.infoq.com/news/2017/03/Java-Finalize-Deprecated – wleao

+0

если что-то есть ссылка к вашему объекту или даже классу. 'finalize()' и сбор мусора не имеет никакого влияния. – ha9u63ar

ответ

213

В общем, лучше не полагаться на finalize(), чтобы делать какие-либо очистки и т.д.

По Javadoc (что было бы целесообразно чтение), это:

Вызывается сборщиком мусора на объекте, когда сборщик мусора определяет ТНА t больше нет ссылок на объект.

Как указал Йоахим, это может никогда не произойти в жизни программы, если объект всегда доступен.

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

+15

Другими словами (просто для уточнения для будущих читателей) он никогда не вызывается в основном классе, так как, когда основной класс закрывается, сбор мусора не требуется. ОС очищает все, что приложение использовало в любом случае. –

+91

«не лучший метод для использования ... если только для этого не существует чего-то определенного». Это предложение относится к 100% всего, поэтому это не полезно. Ответ Йоахима Зауэра намного лучше –

+1

@ Zom-B Ваш пример полезен для разъяснения, но для того, чтобы быть педантичным, предположительно, его можно было бы назвать главным классом, если основной класс создает поток не-демона, а затем возвращается? –

322

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

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

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

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

+0

Я принял ваши котировки абсолютно верно. В противном случае мне нужно увеличить время жизни JVM, пока мой объект не будет уничтожен. Есть ли способ сделать это ??? –

+0

Сэр любезно посмотрите на мой другой вопрос, который я упомянул в Программе –

+1

Какой? У вас есть bunch =) (Кроме того, я согласен. Не используйте финализаторы ... если вы полагаетесь на них, тогда ваш фундаментальный дизайн испорчен. Wether это слабость дизайна или сила, Java не имеют деструкторы C++). – mikek

11

Когда метод finalize() вызван в Java?

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

  • Если объект никогда не становится недоступным, finalize() никогда не будет называться на нем.

  • Если GC не работает, то finalize() никогда не может быть вызван. (Обычно GC работает только тогда, когда JVM решает, что, вероятно, будет достаточно мусора, чтобы сделать его целесообразным.)

  • Может потребоваться более одного цикла GC до того, как GC определит, что конкретный объект недоступен. (Java-GC обычно являются «генераторными» коллекторами ...)

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

(Виртуальная машина Java Spec фактически позволяет в JVM, чтобы никогда финализаторами бежать ... при условии, что он не освобождается пространство, используемое объектами. А JVM, которая была реализована таким образом, будет искалечен/бесполезен, но это поведение «разрешено».)

Результат состоит в том, что нецелесообразно полагаться на завершение, чтобы делать то, что должно быть сделано в определенный временной интервал. Это «лучшая практика», чтобы не использовать их вообще. Должен быть лучше (т.более надежный) способ сделать то, что вы пытаетесь сделать в методе finalize().

Единственное законное использование для финализации - очистка ресурсов, связанных с объектами, которые были потеряны кодом приложения. Даже тогда вы должны попытаться написать код приложения, чтобы он не потерял объекты в первую очередь. (Например, можно использовать Java 7+ примерочных с-ресурсов для обеспечения того, чтобы close() всегда называется ...)


Я создал тестовый класс, который записывает в файл, когда финализации() метод вызывается путем его переопределения. Он не выполнен. Может ли кто-нибудь сказать мне причину, почему он не выполняется?

Трудно сказать, но есть несколько возможностей:

  • объект не мусор, поскольку он по-прежнему доступен.
  • Объект не является сборкой мусора, поскольку GC не запускается до завершения теста.
  • Объект найден GC и помещен в очередь завершения GC, но до завершения теста завершение не завершено.
21

Метод Java finalize() не является деструктором и не должен использоваться для обработки логики, от которой зависит ваше приложение. Спецификация Java заявляет, что нет гарантии, что метод finalize вообще будет вызван во время жизни приложения.

Что вы problably хотите это сочетание finally и метод очистки, как:

MyClass myObj; 

try { 
    myObj = new MyClass(); 

    // ... 
} finally { 

    if (null != myObj) { 
     myObj.cleanup(); 
    } 
} 
64
protected void finalize() throws Throwable {} 
  • каждый класс наследует finalize() метод от java.lang.Объект
  • метод вызывается сборщиком мусора, когда он определяет больше нет ссылки на объект существует
  • метод финализации объекта не выполняет никаких действий, но он может быть переопределен любого класса
  • обычно это должно быть перекрыто вычищать не-Java ресурсы, т.е. закрытие файла
  • если overridding finalize() это хорошая практика программирования использовать примерку всеобъемлющих наконец заявление и в всегда звоните super.finalize(). Это является мера безопасности, чтобы гарантировать, что Вы делаете случайно не пропустите закрытия ресурса, используемых объектами призывающей класс

    protected void finalize() throws Throwable { 
        try { 
         close();  // close open files 
        } finally { 
         super.finalize(); 
        } 
    } 
    
  • любого исключение брошенного finalize() во время сборки мусора останавливает финализации, но в остальных случаях игнорируется

  • finalize() никогда не работать больше, чем когда-то на какой-либо объект

цитировал: http://www.janeg.ca/scjp/gc/finalize.html

Вы также можете проверить эту статью:

+23

В статье JavaWorld, ссылка на которую вы ссылаетесь, находится с 1998 года, и есть несколько интересных советов, в частности предложение о том, что один вызов System.runFinalizersOnExit() обеспечивает выполнение финализаторов до JVM завершает работу. Этот метод в настоящее время не рекомендуется, с комментарием «Этот метод по своей сути является небезопасным. Это может привести к тому, что финализаторы будут вызываться для живых объектов, в то время как другие потоки одновременно манипулируют этими объектами, что приводит к неустойчивому поведению или взаимоблокировке ». Поэтому я не буду этого делать. –

+3

С runFinalizerOnExit() не поточно-то, что можно сделать, это Runtime.getRuntime() addShutdownHook (новая тема() { общественного недействительными запуска() { destroyMyEnclosingClass();} }). в конструкторе Класса. –

+2

@Ustaman Sangat Это способ сделать это, но помните, что он устанавливает ссылку на ваш экземпляр из shutdownHook, что в значительной степени гарантирует, что ваш класс никогда не будет собирать мусор. Другими словами, это утечка памяти. – pieroxy

3

завершить метод не метод guaranteed.This вызывается, когда объект получает право на GC , Существует множество ситуаций, когда объекты не могут быть собраны в мусор.

+1

Неверный. Вы говорите, что объект завершается, когда он становится недоступным. Он фактически вызывается, когда метод фактически собран. –

5

finalize распечатает счет для создания класса.

protected void finalize() throws Throwable { 
    System.out.println("Run F"); 
    if (checkedOut) 
     System.out.println("Error: Checked out"); 
     System.out.println("Class Create Count: " + classCreate); 
} 

главный

while (true) { 
    Book novel=new Book(true); 
    //System.out.println(novel.checkedOut); 
    //Runtime.getRuntime().runFinalization(); 
    novel.checkIn(); 
    new Book(true); 
    //System.runFinalization(); 
    System.gc(); 

Как вы можете видеть. Следование пут показать дс была выполнены первый раз, когда счетчик класса 36.

C:\javaCode\firstClass>java TerminationCondition 
Run F 
Error: Checked out 
Class Create Count: 36 
Run F 
Error: Checked out 
Class Create Count: 48 
Run F 
2

Объект получает право для сбора мусора или GC, если его не достижим из любых живых потоков или любого статического refrences других слов вы может сказать, что объект становится пригодным для сбора мусора, если все его ссылки равны нулю. Циклические зависимости не считаются ссылкой, поэтому, если объект А имеет ссылку на объект B, а объект B имеет ссылку на объект A, и у них нет другой живой ссылки, то оба объекта A и B будут иметь право на сбор мусора. Обычно объект получает право на сбор мусора в Java на следующих случаях:

  1. Все ссылки этого объекта в явном виде установить нулевое значение, например, object = null
  2. Объект создается внутри блока, а эта ссылка выходит из области действия после того, как управление выйдет из этого блока.
  3. Родительский объект установлен в null, если объект имеет ссылку на другой объект, и когда вы устанавливаете ссылочный нулевой объект контейнера, дочерний или содержащий объект автоматически становится пригодным для сбора мусора.
  4. Если у объекта есть только живые ссылки через WeakHashMap, он будет иметь право на сбор мусора.
+0

Если поле «final» объекта используется неоднократно во время медленного вычисления, и объект никогда не будет использоваться после этого, будет ли объект сохранен до последнего, когда исходный код запрашивает это поле, или может копия JIT поле во временную переменную, а затем отказаться от объекта перед вычислением? – supercat

+0

@supercat: оптимизатор может переписать код в форму, в которой объект не создается в первую очередь; в этом случае он может быть завершен сразу после завершения его конструктора, если синхронизация не заставит порядок между использованием объекта и финализатором. – Holger

+0

@Holger: В тех случаях, когда JIT может видеть все, что когда-либо произойдет с объектом между его созданием и отказом, я не вижу причин, по которым JIT должен запускать финализатор раньше. Реальный вопрос будет заключаться в том, что код должен делать, чтобы гарантировать, что финализатор не может запустить в рамках определенного метода. В .NET есть функция 'GC.KeepAlive()', которая ничего не делает, заставляя GC предполагать, что она может использовать объект, но я не знаю такой функции в Java. Для этой цели можно использовать переменную volatile, но использование переменной исключительно для такой цели представляется расточительным. – supercat

17

Заканчивать Эффективное Java, второе издание стр.27 Пункт 7: Избегайте финализаторы

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

Прекратить ресурс, использовать примерочных, наконец, вместо этого:

// try-finally block guarantees execution of termination method 
Foo foo = new Foo(...); 
try { 
    // Do what must be done with foo 
    ... 
} finally { 
    foo.terminate(); // Explicit termination method 
} 
+1

или использовать try-with-resources –

8

Поскольку существует uncertainity в вызывания финализации() метод, с помощью виртуальной машины Java (не уверен в том, Finalize(), который будет отменено, или нет), для учебных целей лучший способ наблюдать, что происходит, когда вызывается finalize(), - заставить JVM вызывать сбор мусора командой System.gc().

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

Пример

class Car { 

    int maxspeed; 

    Car() { 
     maxspeed = 70; 
    } 

    protected void finalize() { 

    // Originally finalize method does nothing, but here we override finalize() saying it to print some stmt 
    // Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection 

     System.out.println("Called finalize method in class Car..."); 
    } 
} 

class Bike { 

    int maxspeed; 

    Bike() { 
     maxspeed = 50; 
    } 

    protected void finalize() { 
     System.out.println("Called finalize method in class Bike..."); 
    } 
} 

class Example { 

    public static void main(String args[]) { 
     Car c = new Car(); 
     c = null; // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it 
     Bike b = new Bike(); 
     System.gc(); // should clear c, but not b 
     for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) { 
      System.out.print("\t" + b.maxspeed); 
      if (b.maxspeed > 50) { 
       System.out.println("Over Speed. Pls slow down."); 
      } 
     } 
    } 
} 

Выходной

Called finalize method in class Car... 
      1  2  3  4  5  6  7  8  9 
    10  11  12  13  14  15  16  17  18  19 
    20  21  22  23  24  25  26  27  28  29 
    30  31  32  33  34  35  36  37  38  39 
    40  41  42  43  44  45  46  47  48  49 
    50  51Over Speed. Pls slow down. 
      52Over Speed. Pls slow down. 
      53Over Speed. Pls slow down. 
      54Over Speed. Pls slow down. 
      55Over Speed. Pls slow down. 
      56Over Speed. Pls slow down. 
      57Over Speed. Pls slow down. 
      58Over Speed. Pls slow down. 
      59Over Speed. Pls slow down. 
      60Over Speed. Pls slow down. 
      61Over Speed. Pls slow down. 
      62Over Speed. Pls slow down. 
      63Over Speed. Pls slow down. 
      64Over Speed. Pls slow down. 
      65Over Speed. Pls slow down. 
      66Over Speed. Pls slow down. 
      67Over Speed. Pls slow down. 
      68Over Speed. Pls slow down. 
      69Over Speed. Pls slow down. 
      70Over Speed. Pls slow down. 

Примечание - Даже после печати Шифрование до 70 и после того, какой объект б не используется в программе, существует неопределенность в том, что b очищается или нет JVM, так как «Вызывается метод finalize в классе Bike ...» не печатается.

+6

Вызов 'System.gc();' не гарантирует, что сбор мусора фактически будет запущен. –

+2

Также не гарантируется, что * вид * коллекции будет запущен. Релевантно, поскольку большинство Java-GC являются коллекторами поколения. –

4

В последнее время борясь с методами финализатора (чтобы избавиться от пулов соединений во время тестирования), я должен сказать, что финализатору не хватает многих вещей. Использование VisualVM для наблюдения, а также использовать слабые ссылки для отслеживания фактического взаимодействия я обнаружил, что следующие вещи истинны в среде Java 8 (Oracle JDK, Ubuntu 15):

  • Доработка не вызывается немедленно Финалайзер (GC часть) индивидуально владеет эталоном неуловимо
  • По умолчанию Мусорные коллекторные пулы недоступны объектам
  • Finalize вызывается массово, указывая на деталь реализации, что есть определенная фаза, сборщик мусора освобождает ресурсы.
  • Calling System.gc() часто не приводит к тому, что объекты чаще всего завершаются, это приводит к тому, что Finalizer быстрее узнает о недоступном объекте.
  • Создание дампа потока почти всегда приводит к запуску финализатора из-за высокой накладной кучи во время выполнения дамп кучи или какой-либо другой внутренний механизм
  • Завершение швов, связанных с требованиями к памяти (освободите больше памяти) или перечнем объектов, помеченных для окончательной обработки, увеличивая определенный внутренний предел. Таким образом, если у вас есть много объектов, которые будут завершены, фаза завершения будет срабатывать чаще и раньше по сравнению с несколькими только
  • Были обстоятельства, при которых System.gc() запускает финализацию напрямую, но только если ссылка была локальной и короткой жизни. Это может быть связано с генерацией.

Заключительные мысли

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

1

класса, где мы переопределить метод финализации

public class TestClass {  
    public TestClass() { 
     System.out.println("constructor"); 
    } 

    public void display() { 
     System.out.println("display"); 
    } 
    @Override 
    public void finalize() { 
     System.out.println("destructor"); 
    } 
} 

шансов метода финализации называют

public class TestGarbageCollection { 
    public static void main(String[] args) { 
     while (true) { 
      TestClass s = new TestClass(); 
      s.display(); 
      System.gc(); 
     } 
    } 
} 

когда память перегружена отвалом объектами на дсе позвонит завершить методу

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

0

Java позволяет объектам реализовывать метод finalize() , который может быть вызван.

метод finalize() вызывается, если сборщик мусора пытается установить .

Если сборщик мусора не работает, метод не вызывается.

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

На практике вы вряд ли сможете использовать его в реальных проектах.

Просто имейте в виду, что он не может быть вызван и что он определенно не будет вызываться дважды. Метод finalize() может начинаться с нуля или один раз.

В следующем коде метод finalize() не производит вывод, когда мы запускаем его с , так как программа выйдет до того, как возникнет необходимость запуска сборщика мусора .

Source

-1

Постарайтесь runiing этой программы для лучшего понимания

public class FinalizeTest 
{  
    static { 
     System.out.println(Runtime.getRuntime().freeMemory()); 
    } 

    public void run() { 
     System.out.println("run"); 
     System.out.println(Runtime.getRuntime().freeMemory()); 
    } 

    protected void finalize() throws Throwable { 
     System.out.println("finalize"); 
     while(true) 
      break;   
    } 

    public static void main(String[] args) { 
      for (int i = 0 ; i < 500000 ; i++) { 
        new FinalizeTest().run(); 
      } 
    } 
} 
1

Иногда, когда он будет уничтожен, объект должен сделать действие. Например, если объект имеет не-java-ресурс, такой как дескриптор файла или шрифт, вы можете проверить, освобождены ли эти ресурсы до уничтожения объекта. Для управления такими ситуациями java предлагает механизм, называемый «финализация». Завершая его, вы можете определить конкретные действия, которые происходят, когда объект собирается быть удален из сборщика мусора. Чтобы добавить финализатор в класс, просто определите метод finalize(). Время выполнения Java вызывает этот метод всякий раз, когда он собирается удалить объект этого класса. В рамках метода () вы указываете действия, которые необходимо выполнить перед уничтожением объекта. Сборщик мусора периодически ищет объекты, которые больше не ссылаются ни на какое рабочее состояние, ни косвенно на любой другой объект со ссылкой. Перед выпуском актива среда выполнения Java вызывает метод finalize() на объекте. финализации() метод имеет следующий общий вид:

protected void finalize(){ 
    // This is where the finalization code is entered 
} 

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

Смежные вопросы