Какова цель завершения в Java?

Мое понимание завершения таково:

Чтобы очистить или восстановить память, которую занимает объект, сборщик мусора вступает в действие. (автоматически вызывается?)

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

Это точное описание завершения?

5 ответов

Решение

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

Завершение предназначалось для очистки ресурсов, полученных объектом (не памяти, а других ресурсов, например, файловых дескрипторов, портов, подключений к БД и т. Д.). Однако, это действительно не сработало:-(

  • это непредсказуемо, когда finalize() будет называться
  • на самом деле, нет никакой гарантии, что finalize() будет называться когда-либо!

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

Из этой статьи:

Любые экземпляры классов, которые реализуют метод finalize(), часто называют объектами finalizable. Они не будут немедленно возвращены сборщиком мусора Java, когда на них больше нет ссылок. Вместо этого сборщик мусора Java добавляет объекты в специальную очередь для процесса завершения. Обычно это выполняется специальным потоком, называемым "обработчиком ссылок" на некоторых виртуальных машинах Java. Во время этого процесса завершения поток "Финализатор" будет выполнять каждый метод finalize() объектов. Только после успешного завершения метода finalize() объект будет передан для сборки мусора Java, чтобы освободить место для "будущей" сборки мусора.

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

Также в этой статье от Sun есть несколько хороших диаграмм, объясняющих процесс.

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

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

Собственно, вот поведение метода finalize():

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

Кроме того, некоторые люди провели тесты производительности и показали, что метод finalize несколько замедляет создание / уничтожение объекта. Я не могу вспомнить источник, поэтому воспринимаю эту информацию как не очень достоверную.:)

Финализация используется для очистки ресурсов, которые не могут быть освобождены сборщиком мусора. Например, рассмотрим программу, которая выделяет (через некоторые native API) ресурсы напрямую из ОС. Обычно это приводит к некоторому "дескриптору" (дескриптор файла UNIX или Windows HANDLE, или что-то подобное):

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    private static native long getHandleFromOS();

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}

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

Для того, чтобы ваш код мог очиститься в таком сценарии, есть finalize метод.

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    protected void finalize() {
        returnHandleToOS(handle);
    }

    private static native long getHandleFromOS();
    private static native void returnHandleToOS(long handle);

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}

Теперь, когда GC освобождает пространство экземпляра оболочки, финализатор проверяет, что ресурс правильно возвращен в ОС.

Все это звучит хорошо, но, как уже отмечали другие, недостатком является то, что финализация по своей сути ненадежна: вы не знаете, когда будет запущен финализатор. Хуже того: нет никаких гарантий, что он вообще будет запущен. Так что лучше всего обеспечить dispose Механизм и использовать финализацию только в качестве сети безопасности на тот случай, если клиенты вашего класса забудут правильно расположить свои ссылки:

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    protected void finalize() {
        if( handle != 0 ) returnHandleToOS(handle);
    }

    public void dispose() {
        returnHandleToOS(handle);
        handle = 0;
    }

    private static native long getHandleFromOS();
    private static native void returnHandleToOS(long handle);

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}
Другие вопросы по тегам