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