Зачем нужен метод finalize в Java
Что проверяют
Знаете ли вы, что finalize() это не деструктор и не способ освобождать ресурсы, а устаревший механизм, которым пользоваться не стоит. Android-разработчика следом обычно спрашивают, как тогда закрывать ресурсы и откуда в крэшах берётся TimeoutException из finalize().
Как отвечать
- что это: метод
Object, который сборщик мусора может вызвать перед освобождением недостижимого объекта. Может, но не обязан: момент не определён, а при завершении процесса вызова может не быть вовсе; - статус: помечен
@Deprecatedс Java 9, а в JDK 18 по JEP 421 финализация целиком объявлена к удалению; - цена для GC: объект с финализатором не освобождается за одну сборку. Сначала он попадает в очередь финализации, после вызова метода его нужно собрать ещё раз. Исключения внутри
finalize()молча проглатываются, а объект можно случайно «воскресить», сохранивthisв поле; - Android и ART: финализаторы выполняются в одном фоновом потоке, а сторожевой поток завершает приложение, если финализатор висит дольше примерно 10 секунд. Отсюда известные крэши вида
TimeoutException: ... finalize() timed out, в том числе на системных классах.
Замена: явный close() через Closeable, try-with-resources в Java или use {} в Kotlin. Ресурсы экрана освобождаются по жизненному циклу: в onStop(), onDestroy() или через LifecycleObserver. Страховкой от забытого закрытия служит java.lang.ref.Cleaner (в Android он есть только на свежих уровнях API) или PhantomReference с ReferenceQueue. Найти незакрытые ресурсы помогает StrictMode с detectLeakedClosableObjects().
Где спотыкаются
Говорят, что finalize() вызывается, когда объект становится недоступен. Гарантии нет ни по времени, ни по самому факту вызова.
Предлагают закрывать в finalize() курсоры и файлы «на всякий случай». В итоге дескрипторы висят до случайной сборки, а на Android добавляется риск таймаута.
Надеются ускорить финализацию вызовом System.gc(). Это лишь подсказка системе, которая ничего не гарантирует.