All 15 Garbage Collection questions
More in Garbage Collection
Advertisement
Back to results for “Resources”
Java › Garbage Collection

What is the purpose of the finalize() method, and why has it been deprecated in favor of try-with-resources and Cleaner in modern Java?

Intermediate

Answer

finalize() was historically intended as a last-resort cleanup hook, invoked by the garbage collector at some indeterminate point before an object's memory is actually reclaimed, giving a chance to release non-memory resources (like file handles or native memory) the object might be holding -- it's now deprecated (as of Java 9, removed entirely as of Java 18) due to serious, well-documented problems: no guarantee it will EVER run promptly (or at all, if the JVM exits first), significant performance overhead for any object overriding it (finalizable objects require an extra GC pass), and the ability for a maliciously or accidentally written finalize() to 'resurrect' an object by creating a new strong reference to it, causing confusing lifecycle bugs -- try-with-resources (for deterministic, immediate cleanup) and java.lang.ref.Cleaner (a safer, non-resurrecting replacement for truly GC-triggered cleanup when try-with-resources genuinely isn't applicable) are the recommended modern alternatives.
// Deprecated/removed approach -- DO NOT USE in modern code
@Deprecated
protected void finalize() throws Throwable {
    closeNativeResource();  // no guarantee of WHEN (or if) this ever actually runs
}

// Modern replacement: Cleaner, for genuine last-resort native resource cleanup
public class NativeResourceHolder implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();
    private final Cleaner.Cleanable cleanable;

    public NativeResourceHolder() {
        long nativeHandle = allocateNative();
        cleanable = cleaner.register(this, () -> freeNative(nativeHandle));
    }
    public void close() { cleanable.clean(); }  // still prefer explicit close() via try-with-resources
}
Real-world example A legacy codebase relying on finalize() to release native file handles experiences a resource exhaustion issue in production (too many handles open simultaneously) traced to finalize() not running promptly enough under GC pressure, resolved by migrating the class to implement AutoCloseable and using try-with-resources for guaranteed, deterministic cleanup instead.

Common follow-ups: How does Cleaner avoid the object-resurrection problem that made finalize() unsafe?;Why is try-with-resources considered fundamentally superior to any GC-triggered cleanup mechanism for resource management?