Когда метод finalize () вызывается в Java? - PullRequest
304 голосов
/ 24 марта 2010

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

Ответы [ 16 ]

363 голосов
/ 24 марта 2010

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

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

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

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

260 голосов
/ 24 марта 2010

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

Согласно Javadoc (который стоило бы прочитать), это:

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

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

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

70 голосов
/ 24 марта 2010
protected void finalize() throws Throwable {}
  • каждый класс наследует метод finalize() от java.lang.Object
  • метод вызывается сборщиком мусора, когда он не определяет больше ссылок наобъект существует
  • метод Object finalize не выполняет никаких действий, но он может быть переопределен любым классом
  • обычно он должен быть переопределен для очистки ресурсов, отличных от Java, то есть закрытия файла
  • при переопределении finalize() хорошей практикой программирования является использование оператора try-catch-finally и всегда вызывать 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 голосов
/ 24 марта 2010

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

Вероятно, вам понадобится комбинация finally и метод очистки, например:

MyClass myObj;

try {
    myObj = new MyClass();

    // ...
} finally {

    if (null != myObj) {
        myObj.cleanup();
    }
}
19 голосов
/ 01 июля 2013

Ознакомьтесь с Effective Java, 2-е издание, стр. 27. Пункт 7: Избегайте финализаторов

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

Чтобы завершить ресурс, используйте вместо этого try-finally:

// 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
}
16 голосов
/ 24 марта 2010

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

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

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

  • Если ГХ не запускается, то finalize() никогда не может быть вызван. (Как правило, сборщик мусора запускается только тогда, когда JVM решает, что мусора может быть достаточно, чтобы он оправдал себя.)

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

  • Как только ГХ обнаруживает, что объект недостижим и завершен, он помещается в очередь завершения. Завершение обычно происходит асинхронно с обычным GC.

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

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

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


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

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

  • Объект не является сборщиком мусора, поскольку он все еще доступен.
  • Объект не является сборщиком мусора, поскольку сборщик мусора не запускается до завершения теста.
  • Объект найден GC и помещен в очередь завершения GC, но завершение не завершено до завершения теста.
10 голосов
/ 13 мая 2015

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

В частности, finalize () вызывается, когда объект больше не используется. Но когда мы пытаемся вызвать его, создавая новые объекты, нет уверенности в его вызове. Поэтому для определенности мы создаем null объект c, который, очевидно, не будет использоваться в будущем, поэтому мы видим завершающий вызов объекта 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 не используется в программе, существует неопределенность, что b очищается или нет JVM, так как "вызван метод финализации в классе Bike .. . "не печатается.

5 голосов
/ 25 ноября 2012

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();

Как видите. Следующий вывод показывает, что 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
4 голосов
/ 25 сентября 2015

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

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

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

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

2 голосов
/ 02 июля 2017

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

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

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

...