← Назад к вопросам

Что такое Finalize?

2.3 Middle🔥 131 комментариев
#JVM и управление памятью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Finalize

Finalize - это метод в Java, который вызывается garbage collector перед тем, как объект будет окончательно удалён из памяти. Это механизм для очистки ресурсов, но он морально устарел и его использование не рекомендуется в современном Java.

Базовое определение

public class FileHandler {
    private FileInputStream fis;
    
    public FileHandler(String filename) throws IOException {
        this.fis = new FileInputStream(filename);
    }
    
    // finalize вызовется перед удалением объекта
    @Override
    protected void finalize() throws Throwable {
        try {
            if (fis != null) {
                fis.close(); // Закрываем ресурс
            }
        } finally {
            super.finalize();
        }
    }
}

Как это работает

  1. Объект становится недостижимым (нет на него ссылок)
  2. GC отмечает его для удаления
  3. Перед окончательным удалением GC вызывает finalize()
  4. Только после этого объект удаляется из памяти
public class FinalizationExample {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj = null; // Объект больше не достижим
        
        System.gc(); // Явно просим запустить GC
        Thread.sleep(1000); // Ждём пока GC выполнится
        
        // Метод finalize() будет вызван где-то между
        // obj = null и этой строкой
    }
}

public class MyClass {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize() called!");
        super.finalize();
    }
}

Проблемы с Finalize

1. Недетерминированное время выполнения

public class ResourceLeak {
    private DatabaseConnection connection;
    
    public ResourceLeak() throws SQLException {
        this.connection = DriverManager.getConnection("jdbc:...");
    }
    
    @Override
    protected void finalize() throws Throwable {
        try {
            connection.close(); // Когда это выполнится? Неизвестно!
        } finally {
            super.finalize();
        }
    }
}

// Проблема:
// Если создаёшь 1000 объектов:
for (int i = 0; i < 1000; i++) {
    new ResourceLeak(); // Соединения открыты, но не закрыты
    // finalize() может вызваться намного позже
    // А база имеет лимит на количество соединений!
    // Может быть OutOfConnectionsException
}

2. Производительность

// Объекты с finalize() требуют дополнительных действий от GC
public class FinalizationOverhead {
    // Каждый такой объект добавляет работу для Finalizer thread
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
    }
}

// GC делает:
// 1. Отмечает объект как "мусор"
// 2. Добавляет его в очередь для финализации
// 3. Finalizer thread вызывает finalize()
// 4. После этого объект реально удаляется
// = ДОПОЛНИТЕЛЬНЫЙ GC цикл!

3. Finalizer thread может быть перегружен

public class FinalizerThreadProblem {
    private byte[] largeBuffer;
    
    public FinalizerThreadProblem() {
        this.largeBuffer = new byte[100 * 1024 * 1024]; // 100 MB
    }
    
    @Override
    protected void finalize() throws Throwable {
        // Если создавать объекты быстрее чем Finalizer может их удалять
        // Finalize queue переполнится!
        System.out.println("Finalizing...");
        super.finalize();
    }
    
    public static void main(String[] args) {
        for (int i = 0; i < 10000; i++) {
            new FinalizerThreadProblem();
            // Finalizer thread не успевает удалять
            // OutOfMemoryError: GC overhead limit exceeded
        }
    }
}

4. Исключения в finalize() молча игнорируются

public class FinalizationException {
    @Override
    protected void finalize() throws Throwable {
        throw new RuntimeException("Ошибка в finalize!");
        // Эта ошибка просто выведется в stderr
        // И тихо подавится
        // Ресурс может остаться незакрытым!
    }
}

ПРАВИЛЬНЫЙ подход: Try-with-resources

// Вместо finalize() используй try-with-resources (Java 7+)
public class FileHandler implements AutoCloseable {
    private FileInputStream fis;
    
    public FileHandler(String filename) throws IOException {
        this.fis = new FileInputStream(filename);
    }
    
    @Override
    public void close() throws IOException {
        if (fis != null) {
            fis.close();
        }
    }
    
    public static void main(String[] args) {
        // Ресурс ГАРАНТИРОВАННО закроется
        try (FileHandler handler = new FileHandler("file.txt")) {
            // Используем файл
        } // close() вызовется здесь СРАЗУ же
    }
}

Альтернатива: Finally блок

public class ManualResourceManagement {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("file.txt");
            // Используем файл
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // Гарантированно выполнится
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Альтернатива: Cleaner (Java 9+)

import java.lang.ref.Cleaner;

public class ResourceWithCleaner {
    private static final Cleaner CLEANER = Cleaner.create();
    
    private final Cleaner.Cleanable cleanable;
    private final Resource resource;
    
    public ResourceWithCleaner(String filename) throws IOException {
        this.resource = new Resource(filename);
        this.cleanable = CLEANER.register(this, new CleaningAction(resource));
    }
    
    public void close() {
        cleanable.clean(); // Явно вызываем очистку
    }
    
    private static class CleaningAction implements Runnable {
        private final Resource resource;
        
        CleaningAction(Resource resource) {
            this.resource = resource;
        }
        
        @Override
        public void run() {
            resource.cleanup();
        }
    }
    
    public static void main(String[] args) throws IOException {
        try (ResourceWithCleaner r = new ResourceWithCleaner("file.txt")) {
            // Используем ресурс
        } // close() вызовется автоматически
    }
}

Исторический контекст

// До Java 7: finalize() был единственным способом
public class LegacyCode {
    private Connection connection;
    
    public LegacyCode() throws SQLException {
        connection = DriverManager.getConnection("jdbc:...");
    }
    
    @Override
    protected void finalize() throws Throwable {
        try {
            connection.close();
        } finally {
            super.finalize();
        }
    }
}

// Java 7+: try-with-resources делает это ненужным
public class ModernCode implements AutoCloseable {
    private Connection connection;
    
    public ModernCode() throws SQLException {
        connection = DriverManager.getConnection("jdbc:...");
    }
    
    @Override
    public void close() throws SQLException {
        connection.close();
    }
    
    public static void main(String[] args) {
        try (ModernCode code = new ModernCode()) {
            // Используем
        } // Гарантированно закроется
    }
}

Deprecation в Java 18+

// Java 18+ deprecated finalize()
// В future версиях Java может быть удалён полностью

@Deprecated(since="18", forRemoval=true)
protected void finalize() throws Throwable {
    super.finalize();
}

Когда finalize() может быть полезен (редко)

// Только для safety check в debug режиме
public class DebugResource implements AutoCloseable {
    private boolean closed = false;
    
    @Override
    public void close() {
        closed = true;
    }
    
    @Override
    protected void finalize() throws Throwable {
        // ТОЛЬКО для отладки!
        if (!closed) {
            System.err.println("WARNING: Resource was not closed!");
        }
        super.finalize();
    }
}

Best Practices

✗ НИКОГДА не используй finalize() в production ✓ Используй try-with-resources (Java 7+) ✓ Реализуй AutoCloseable для управления ресурсами ✓ Используй try-finally для старого кода ✓ Если нужно - используй Cleaner (Java 9+) ✓ Всегда явно закрывай ресурсы ✓ Не полагайся на GC для управления ресурсами

Finalize - это пережиток прошлого Java. Используй современные инструменты для управления ресурсами!

Что такое Finalize? | PrepBro