skip to content

finalize (Deprecated)

finalize runs at the garbage collector's discretion, with no timing guarantee, a resurrection hazard and a real performance cost, and has been deprecated since Java 9. The expected answer is what to use instead: try-with-resources, and Cleaner or PhantomReference for the rest.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is the finalize() method in Java, and when is it called?

level: juniorimportance: should knowfreq 45%

answer

  1. Object.finalize, protected, runs at most once
  2. GC-timed, no guarantee, may never run
  3. Two GC cycles to die
  4. Deprecated Java 9, removal JEP 421
  5. Replace: try-with-resources, Cleaner

basics

~10 s

finalize() is a method on java.lang.Object that the garbage collector may call once before it reclaims an object's memory, to let the object clean up. You can't control when (or even whether) it runs.

solid answer

~40 s

finalize() is a protected, no-argument method inherited from java.lang.Object. The idea was to give an object a last chance to release resources (like file handles or native memory) just before the garbage collector frees it. The GC calls it at most once per object, on a background finalizer thread, only after deciding the object is unreachable. The crucial caveat is that there is no guarantee about timing or even execution at all: an object may sit in the queue for a long time, and if the JVM exits first, finalize() may never run. Because of this unpredictability, plus performance and security problems, it has been deprecated since Java 9 and removed as an overridable hook in newer releases. Modern code uses try-with-resources / AutoCloseable for deterministic cleanup and java.lang.ref.Cleaner for safety-net native cleanup.

go deeper

for a junior

Knows finalize() exists on Object, is called by the GC before an object is collected, and that its timing is not guaranteed. Can say 'use try-with-resources instead.'

for a middle

Explains the at-most-once contract, the two-GC-cycle cost, that it may never run if the JVM exits, and names AutoCloseable/Cleaner as replacements.

for a senior

Articulates all four flaws (timing, performance, resurrection, exception swallowing), the deprecation timeline, and when (rarely) a Cleaner-based safety net is appropriate vs deterministic close().

for a principal

Frames finalization as a platform anti-pattern being removed (JEP 421), reasons about native-resource ownership/lifecycle design, security implications of finalizer attacks, and sets a codebase-wide policy banning finalize() in favor of AutoCloseable + Cleaner.

## What finalize() is **Java** is a *garbage-collected* language: you allocate objects with `new`, and a background subsystem called the **garbage collector (GC)** automatically frees the memory of objects that are no longer **reachable** (no live part of the program can still refer to them). You never call `free`/`delete` yourself. `finalize()` was an early attempt to bolt C++-style *destructors* onto this model. Every Java class implicitly extends `java.lang.Object`, which historically declared: ```java protected void finalize() throws Throwable { } ``` A class could **override** (provide its own version of) this method. The contract was: *just before* the GC reclaims an object that overrides `finalize()`, the JVM will invoke that method once, giving the object a final chance to release resources it owned — for example closing an open OS **file handle** or freeing **native memory** (memory allocated outside the Java heap by C/C++ code). ## How the JVM actually runs it Finalization is a multi-step, *asynchronous* dance, not a simple call: 1. The GC discovers the object is unreachable. 2. If the object overrides `finalize()`, the GC does **not** free it yet. Instead it places the object on an internal **finalization queue** and keeps it alive. 3. A separate, low-priority **finalizer thread** later pulls objects off that queue and runs their `finalize()` methods. 4. Only on a *subsequent* GC cycle, once the object is unreachable *again*, is its memory actually reclaimed. So a finalizable object needs **at least two GC cycles** to die, and its cleanup runs on a thread you don't control, at a time you can't predict. ## Why this is a problem (the four classic flaws) - **No timing guarantee.** The **JLS (Java Language Specification)** promises only that `finalize()` runs *at most once*, *before* reclamation — never *when*. The finalizer thread may be starved, the queue may back up, and crucially **if the JVM exits, pending finalizers may never run at all**. So you can't use it to flush data or close a network connection reliably. - **Performance penalty.** Keeping objects alive an extra cycle, queueing them, and running them on a single thread makes finalizable objects dramatically slower to create and collect (commonly cited as roughly an order of magnitude). They also delay reclamation, raising memory pressure. - **The resurrection problem.** Because `finalize()` runs arbitrary code with `this` in scope, it can store `this` into a static field or other live structure — making the *supposedly dead* object reachable again ("resurrected"). The object is now alive, but the JLS says `finalize()` will **never run again** for it, so a second cleanup is silently skipped. This is a footgun, not a feature. - **Reliability / exception swallowing.** If `finalize()` throws an exception, the finalizer thread simply logs nothing useful and *abandons* finalization of that object — the rest of the cleanup is skipped, often leaving the object in a corrupt state with no warning. There are also security concerns (a malicious subclass overriding `finalize()` can interfere with a half-constructed object after its constructor throws), which is why security-sensitive classes use a *finalizer guardian*. ## Deprecation timeline - **Java 9:** `Object.finalize()` was marked `@Deprecated`. - Later releases (`Java 18`, via **JEP 421**) deprecated finalization **for removal**, with flags to disable it, and it is being phased out of the platform entirely. ## What to use instead 1. **`try-with-resources` + `AutoCloseable`** — the *deterministic* solution. A class that owns a resource implements `java.lang.AutoCloseable` (or `java.io.Closeable`) and puts cleanup in `close()`. The caller wraps it in `try (var r = new Resource()) { ... }`; the compiler guarantees `close()` runs at the end of the block, exactly when you expect, even on exception. This is how every modern stream/connection should be managed. ```java try (var in = new FileInputStream("data.bin")) { // use in } // in.close() called here, deterministically ``` 2. **`java.lang.ref.Cleaner`** (Java 9+) — the *safety-net* solution for native/off-heap resources, when you also want cleanup if the caller forgets to call `close()`. You register a cleanup action (a `Runnable` that must **not** reference the owning object, to avoid keeping it alive) with a shared `Cleaner`; when the object becomes unreachable, the cleaner runs the action on its own thread. It is built on `PhantomReference` and is more predictable and less dangerous than `finalize()` — but still not a substitute for deterministic `close()`. Treat it strictly as a backstop. **Bottom line:** never write a new `finalize()`. Use `try-with-resources` for deterministic cleanup, and a `Cleaner` only as a last-resort safety net for native resources.

  • Why can't you rely on finalize() to close a file handle?
    Because the GC decides when (or whether) finalize() runs. The file could stay open indefinitely under low memory pressure, and if the JVM exits before the finalizer thread runs, the handle is never closed. Deterministic cleanup needs try-with-resources/close().
  • How many garbage-collection cycles does a finalizable object take to be reclaimed?
    At least two: one cycle detects it as unreachable and queues it for finalization (keeping it alive), and a later cycle actually reclaims it after finalize() has run and it is unreachable again.

saying these in an interview costs you the question

  • Claiming finalize() is guaranteed to run before the program exits
  • Saying finalize() is like a C++ destructor that runs deterministically
  • Using finalize() to close files or flush buffers in new code
  • Thinking finalize() runs immediately when the object goes out of scope

context

open as a page

Why was finalize() deprecated, and what are its replacements for resource cleanup?

level: middleimportance: should knowfreq 50%

basics

~20 s

finalize() is unreliable: you can't predict when it runs, it may never run, it slows down GC, and it has dangerous edge cases. Java deprecated it in 9. Use try-with-resources (AutoCloseable) for deterministic cleanup and Cleaner as a safety net.

open as a page

How does Java's finalize() differ from a C++ destructor, and why doesn't Java have deterministic destructors?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

A C++ destructor runs at a predictable moment (when the object goes out of scope or is deleted). Java's finalize() runs whenever the garbage collector decides to, maybe never. Because Java auto-manages memory with a GC, it has no deterministic destructor; you use try-with-resources for predictable cleanup.

open as a page

Explain the object resurrection problem with finalize(), and why an object's finalize() never runs a second time.

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

Inside finalize(), the object can store 'this' somewhere reachable, making itself live again (resurrected). Because finalize() runs at most once per object, the JVM won't call it again later, so any future cleanup is silently skipped.

open as a page

You own native (off-heap) memory in a Java class. Design a cleanup strategy without finalize(). What are the key pitfalls?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Make the class AutoCloseable and free the native memory in close(); callers use try-with-resources. Add a Cleaner as a safety net for forgotten closes, but make the cleanup action a separate object that holds only the native pointer, never a reference back to your class.

open as a page