Pull to refresh

Comments 26

Как вы считаете, вообще пользоваться finalize имеет смысл? Ведь это ставит логику приложения в зависимость от работы сборщика мусора. Разве это правильно?
Имеет смысл всегда явно закрывать ресурсы, не полагаясь на finalize вообще. В Java 1.7 для этого даже есть удобная штука try-with-resources. В принципе, конечно, можно вообще отказаться от finalize, надеясь на добросовестность разработчика. Другое дело, что многие библиотеки (в том числе системные) его всё же используют, и с этим приходится считаться.
Есть особый сценарий использования, когда вы имеете объекты, связанные через WeakReference/SoftReference. Скажем, объект, достающий информацию из какого-то большого файла (так что весь файл загрузить и закрыть его нельзя). Объект держит файл открытым, чтобы при запросах быстро спозиционироваться и достать нужную запись, но при этом при нехватке памяти вы не возражаете, если он будет уничтожен (но файл надо закрыть). При следующем обращении к файлу вы просто снова создадите такой объект. Если отказаться от finalize(), с такого рода объектами жизнь станет сложнее: придётся отслеживать ReferenceQueue, возиться с фантомными ссылками и т. д.
Спасибо, интересный подход. Вообще я программирую на AS3 и у нас нет такого метода, а слабые ссылки есть, поэтому в отсутствии finalize слабыми ссылками я тоже не пользуюсь.

Вообще-то, при таком сценарии файл будет закрыт когда файловый поток будет собран сборщиком мусора. То есть не требуются ни метод finalize, ни фантомные ссылки, ни ReferenceQueue.

Естественно. Только сборщик мусора срабатывает, когда памяти мало, ему плевать, если у вас мало файл-дескрипторов (нередка ситуация, когда процесс не может открыть, например, больше 1000 файлов, потому что ulimit так настроен). Это бомба замедленного действия, когда вы полагаетесь на сборщик мусора с открытыми файлами. Когда она выстрелит у каких-нибудь клиентов, вы поймёте, что чтобы исправить эту проблему, переделывать придётся слишком много.

Нет никакой разницы между


private FileInputStream file;

protected void finalize() throws Throwable {
    file.close();
}

и тем же самым кодом, но без finalize(). Когда объект уже собран сборщиком мусора — закрывать управляемые ресурсы поздно!

Я абсолютно не понимаю, зачем вы выделили "уже" курсивом и полужирным и как это относится к моему комментарию. А разница определённо есть: здесь вам потребуется три цикла сборки мусора, чтобы всё прибрать. А без finalize() только два.

То есть вы тоже согласны, что finalize() тут лишний? Тогда как вы собрались с помощью finalize делать кеширующий читатель большого файла?

Под "отказаться от finalize" подразумевалось не использовать его в том числе в объекте, который непосредственно держит ресурс (например, FileInputStream). Хотя это было писано четыре года назад. Сейчас PhantomReference даже в данном случае выглядит привлекательнее. Тем более, в Java 9 появится публичный Cleaner, который гораздо лучше finalize.

Иногда проще подержать некритичные ресурсы открытыми, чем реализовывать подсчет ссылок
В одном крупном и достаточно популярном open-source проекте встречал следующее:

protected void finalize() throws Throwable {
    this.rows = null;
    this.tables = null;
    this.frame = null;
    ....
}


Т.е. программист сознательно «занулял» все поля объекта. Сдается мне в корпоративных приложениях на вроде «Система внутреннего документа оборота ТраснКредитСельхозУнипроЛевобережного Банка» таких вещей оч. много.
UFO just landed and posted this here
Ценой того, что родительский объект удаляется не сразу, а в два прохода, между которыми надо отстоять очередь Finalizer'а? Это может быть оправдано для стековых переменных, но в данном случае — очень сомневаюсь.
Конечно, в этом нет ничего плохого — это полный п ужас!
1. Никогда, никогда не помогайте GC (особенно через finalize), только хуже сделаете.
2. Если возможно (т.е. вы не завязаны на legacy code) — не юзайте finalize(), мы кричим об этом годами, но никто не слышит.
3. Если все-таки необходимо завязываться на освобождение ресурсов — только <*>Reference. Не забудьте, * — подставить Soft/Weak/Phantom — по необходимости.
UFO just landed and posted this here
Есть.
Финализатор — это плохо.
Финализатор «помогающий GC» — это еще хуже.
Не понимаю, за что этому коменту поставили минус и, к сожалению, не могу это поправить. Но ведь человек абсолютно прав — PhantomReference были придуманы как альтернатива finalize. И если finalize требует каких-то длительных вычислений, то рекомендуется переписать логику с использованием PhantomReference — в таком случае можно будет самому следить за очередью объектов на очистку и самому выполнять чистку в отдельном пуле потоков.
Наверное, минус поставили те, кто думает, что PhantomReference – это такая неудачная шутка. Однако, фантомы вполне реальны.

Немного разъясню вашу мысль. Давайте сделаем вот так:
ReferenceQueue queue = new ReferenceQueue();
PhantomReference ref = new PhantomReference(object, queue);

После того, как object будет уже никому не нужен он добавится в queue. Его можно будет забрать вот так:
PhantomReference ref = queue.remove();

Понятно, что забирать и обрабатывать эти ссылки можно в своей нити. Стоит отметить, что ref.get() всегда возвращает null, хотя object и жив внутри приватного поля ссылки.

Этот механизм позиционируется как более гибкий, чем finalize(). Хотя, если честно, когда я вижу такие кренделя в неспециальных проектах у меня по коже муражки идут.
UFO just landed and posted this here
Совершенно верно. Это одно из основных применений finalize. Например, когда нужно отлавливать «брошенный» ресурс, чтобы вернуть его обратно в пул.
UFO just landed and posted this here
Спасибо автору. Интересный прием с пустым finalize(){}; Конечно с оговорками, которые были упомянуты в посте.
Finalize конечно, в большом мире java достаточно редко используемая вешь, но где используется Week/SoftReference вполне возможная ситуация, когда finalize будет необходим.
За пост — пять!
Для этого есть PhantomReference.
Sign up to leave a comment.

Articles