Какова цель завершения в Java? - PullRequest
11 голосов
/ 16 марта 2010

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

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

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

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

Ответы [ 5 ]

15 голосов
/ 16 марта 2010

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

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

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

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

7 голосов
/ 16 марта 2010

С этой статьи :

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

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

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

6 голосов
/ 16 марта 2010

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

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

5 голосов
/ 16 марта 2010

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

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

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

2 голосов
/ 16 марта 2010

Финализация используется для очистки ресурсов, которые не могут быть освобождены сборщиком мусора. Например, рассмотрим программу, которая выделяет (через некоторые 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());
    }
}
...