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

Мое понимание финализации таково:

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

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

Является ли это точным описанием завершения?

Ответ 1

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

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

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

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

Ответ 2

Из в этой статье:

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

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