Почему в современном Java не рекомендуется полагаться на метод finalize() для освобождения ресурсов?

«Почему в современном Java не рекомендуется полагаться на метод finalize() для освобождения ресурсов?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Метод finalize() из класса Object — это механизм, который сборщик мусора (GC) может вызвать перед удалением объекта. Однако его использование считается устаревшей и опасной практикой (deprecated since Java 9).

Критические недостатки finalize():

  1. Нет гарантии вызова: GC может никогда не вызвать finalize(), если память не требуется или приложение завершает работу.
  2. Непредсказуемая задержка: Объект, имеющий finalize(), уничтожается минимум за два цикла GC, что приводит к задержкам в освобождении памяти и ресурсов.
  3. Производительность: Сильно замедляет работу сборщика мусора.
  4. Исключения игнорируются: Любое исключение, выброшенное в finalize(), игнорируется, что может скрыть ошибки.
  5. Проблемы с порядком: Нет контроля над порядком вызова finalize() для взаимосвязанных объектов.

Пример проблемного кода:

@Override
protected void finalize() throws Throwable {
    try {
        file.close(); // Ненадежно! Может не вызваться или вызваться с большой задержкой.
    } finally {
        super.finalize(); // Обязательный вызов в старом коде.
    }
}

Современная альтернатива — интерфейс AutoCloseable:

public class ResourceHandler implements AutoCloseable {
    private FileInputStream file;

    public ResourceHandler(String path) throws FileNotFoundException {
        this.file = new FileInputStream(path);
    }

    @Override
    public void close() { // Вызывается явно или автоматически
        if (file != null) {
            try {
                file.close();
            } catch (IOException e) {
                // Логирование ошибки
            }
        }
    }
}

// Использование с try-with-resources (гарантированный вызов close())
try (ResourceHandler handler = new ResourceHandler("data.txt")) {
    // работа с ресурсом
} // close() вызывается здесь автоматически

Вывод: Всегда используйте try-with-resources и интерфейс AutoCloseable для управления ресурсами. Метод finalize() следует избегать.