Спецификация Java: пункт 12.6.1. Реализация финализации

Каждый объект может быть охарактеризован двумя атрибутами: он может быть доступен, доступен для финализатора или недоступен, а также может быть нефинализируемым, финализируемым или финализированным.

Доступный объект - это любой объект, к которому можно получить доступ в любом потенциальном продолжающемся вычислении из любого живого потока.

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

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

Для нефинализированного объекта финализатор никогда не вызывался автоматически.

У финализированного объекта был автоматически вызван финализатор.

У финализируемого объекта никогда не было автоматического вызова финализатора, но виртуальная машина Java может в конечном итоге автоматически вызвать его финализатор.

Объект o не может быть финализирован до тех пор, пока его конструктор не вызовет конструктор для Object на o и этот вызов не завершится успешно (то есть без создания исключения). Каждая префинализируемая запись в поле объекта должна быть видимой для финализации этого объекта. Более того, ни одно из префинализируемых чтений полей этого объекта не может видеть записи, которые происходят после инициализации финализации этого объекта.

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

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

Например, рассмотрим паттерн Finalizer Guardian (защитник финализатора):

class Foo {
    private final Object finalizerGuardian = new Object() {
        protected void finalize() throws Throwable {
            /* финализируем внешний Foo объект */
        }
    }
} 

Защитник финализатора принудительно вызывает super.finalize, если подкласс переопределяет finalize и не вызывает явно super.finalize.

Если эти оптимизации разрешены для ссылок, которые хранятся в куче, то компилятор Java может обнаружить, что поле finalizerGuardian никогда не читается, обнулить его, немедленно собрать объект и вызвать финализатор раньше. Это идет вразрез с намерением: программист, вероятно, хотел вызвать финализатор Foo, когда экземпляр Foo стал недоступен. Следовательно, такого рода преобразование недопустимо: объект внутреннего класса должен быть доступен до тех пор, пока доступен объект внешнего класса.

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

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


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки