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

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

19 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

То, что вы, вероятно, хотите, это сочетание finally и метод очистки, как в:

MyClass myObj;

try {
    myObj = new MyClass();

    // ...
} finally {

    if (null != myObj) {
        myObj.cleanup();
    }
}

Ознакомьтесь с 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
}

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

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

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

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

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

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

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

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

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


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

Сложно сказать, но есть несколько возможностей:

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

Так как существует неопределенность в вызове метода 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..." не печатается.

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

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

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

Последняя мысль

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

Последние новости с JDK 18

Согласно JEPS 421 , поставленному на openjdk 18, завершение и, следовательно, функциональность finalize()метод будет помечен как deprecated(forRemoval=true)это означает, что окончательное удаление последует в какой-то более поздней версии после jdk 18.

Начиная с jdk 18, новая опция командной строки --finalization=disabledотключает механизм финализации везде, даже для объявлений внутри самого jdk.

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

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

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

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

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

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

Методфинализации не гарантирован. Этот метод вызывается, когда объект становится подходящим для GC. Есть много ситуаций, когда объекты не могут быть собраны мусором.

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

Пример:

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

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

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

когда память перегружена объектами дампа, gc вызовет метод finalize

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

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

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

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

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

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

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

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

Источник

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

Подкласс переопределяет метод finalize для удаления системных ресурсов или выполнения другой очистки.

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

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

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

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

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

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

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

Как указано в https://wiki.sei.cmu.edu/confluence/display/java/MET12-J.+Do+not+use+finalizers,

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

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

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();
            }
     }
}
Другие вопросы по тегам