Java tutorials > Java Virtual Machine (JVM) > Memory Management and Garbage Collection > Role of `finalization`?
Role of `finalization`?
Finalization in Java refers to the process that allows an object to perform cleanup activities before it is garbage collected. The `finalize()` method, defined in the `Object` class, is the mechanism through which this is achieved. However, it's crucial to understand that finalization is generally discouraged in modern Java development due to its unpredictable nature, performance overhead, and potential for preventing garbage collection. Newer alternatives like try-with-resources and cleaners provide more reliable and efficient resource management.
Understanding `finalize()`
The `finalize()` method is a protected method inherited from the `Object` class. It's intended to be overridden in subclasses to provide a mechanism for cleaning up resources held by an object before it's garbage collected. In the example, the `MyResource` class overrides `finalize()` to release a simulated resource. Note that calling `System.gc()` only suggests that the JVM run the garbage collector; it's not guaranteed. The `Thread.sleep()` call gives the garbage collector a chance to execute before the program terminates. The order and timing of `finalize()` calls are unpredictable. Furthermore, exceptions thrown during finalization are generally ignored by the JVM, which can lead to resource leaks.
class MyResource {
// Resource representing a file, database connection, etc.
private String resourceName;
public MyResource(String name) {
this.resourceName = name;
System.out.println("Resource " + resourceName + " created.");
}
// Simulate releasing the resource
public void releaseResource() {
System.out.println("Releasing resource " + resourceName + ".");
}
@Override
protected void finalize() throws Throwable {
try {
releaseResource();
} finally {
super.finalize();
}
}
public static void main(String[] args) {
MyResource resource1 = new MyResource("File1");
MyResource resource2 = new MyResource("DatabaseConnection");
resource1 = null; // Make the objects eligible for garbage collection
resource2 = null;
System.out.println("Objects are now eligible for garbage collection.");
// Suggest garbage collection (not guaranteed to run immediately)
System.gc();
// Wait for a while to allow garbage collection to potentially happen
try {
Thread.sleep(2000); // Sleep for 2 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Program finished.");
}
}
Concepts Behind the Snippet
The core concept behind finalization is providing a last-ditch effort to clean up resources that were not explicitly released by the programmer. However, the unpredictable timing and the fact that finalization is not guaranteed to run make it unreliable. The object may become eligible for garbage collection but never be finalized, leading to resource leaks or other issues. Moreover, resurrecting an object within a `finalize()` method (making it reachable again) can lead to complex and hard-to-debug issues. The JVM only calls `finalize()` once per object instance, even if resurrected and subsequently garbage collected.
Real-Life Use Case (Generally Discouraged)
While finalization is generally discouraged, one hypothetical use case, although strongly discouraged, could be for releasing native resources that are not directly managed by Java, such as file descriptors or socket connections held by native libraries accessed via JNI (Java Native Interface). However, even in this scenario, it is far better to use explicit resource management techniques. The problem is that if the garbage collector doesn't run quickly enough, you can exhaust those limited resources.
Best Practices (Avoid `finalize()` where possible)
The best practice is to avoid relying on finalization whenever possible. Instead, use more deterministic and reliable resource management techniques such as:
Interview Tip
When discussing finalization in a Java interview, emphasize that it's generally discouraged and unreliable. Explain the reasons for avoiding it, such as its unpredictable timing, potential performance overhead, and the risk of preventing garbage collection. Highlight the preferred alternatives like try-with-resources and cleaners, and explain why they provide better resource management.
When to use them (Rarely, if ever)
The recommendation is to almost never use `finalize()`. The circumstances where `finalize()` might seem necessary can almost always be handled more effectively using other mechanisms. Specifically, it should never be relied upon for critical resource management.
Memory Footprint
Using `finalize()` can increase the memory footprint of your application. Objects with finalizers require special handling by the garbage collector, which can delay their collection and increase memory usage. Finalizable objects are placed on a finalization queue and processed by a finalizer thread which consumes memory. This adds overhead compared to objects without finalizers.
Alternatives: try-with-resources
The try-with-resources statement ensures that resources that implement the `AutoCloseable` interface (which includes most I/O resources like files and sockets) are automatically closed when the try block exits, regardless of whether an exception is thrown. This provides a reliable and deterministic way to manage resources.
import java.io.*;
public class TryWithResourcesExample {
public static void main(String[] args) {
// Automatically closes the file after use, even if exceptions occur
try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Alternatives: Cleaners (Java 9+)
Cleaners provide a more controlled way to handle cleanup actions when an object becomes unreachable. The `Cleaner` class registers a `Runnable` (the 'State' inner class here) that will be executed when the associated object is garbage collected. This is generally safer and more predictable than finalization.
import java.lang.ref.Cleaner;
class MyResource implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
private final State state;
private final Cleaner.Cleanable cleanable;
static class State implements Runnable {
String resourceName;
State(String resourceName) {
this.resourceName = resourceName;
System.out.println("Resource " + resourceName + " created.");
}
@Override
public void run() {
System.out.println("Releasing resource " + resourceName + " via Cleaner.");
}
}
public MyResource(String resourceName) {
this.state = new State(resourceName);
this.cleanable = cleaner.register(this, state);
}
@Override
public void close() {
cleanable.clean();
}
public static void main(String[] args) {
try (MyResource resource = new MyResource("File1")) {
// Use the resource
} // Resource is automatically closed here
}
}
Pros (of `finalize()`, minimal and mostly theoretical)
Theoretically, `finalize()` could be used as a last resort to prevent resource leaks if other resource management techniques fail. However, this benefit is outweighed by the drawbacks.
Cons (of `finalize()`, significant)
FAQ
-
Why is `finalize()` discouraged?
`finalize()` is discouraged due to its unpredictable timing, performance overhead, the possibility of preventing garbage collection, and the fact that exceptions thrown during finalization are generally ignored. More reliable and efficient resource management techniques, such as try-with-resources and cleaners, are preferred.
-
What is the difference between `finalize()` and `close()`?
`close()` is a method you explicitly call to release resources, providing deterministic resource management. `finalize()` is a method called by the garbage collector (if and when it chooses to) and is unreliable for resource management. `close()` provides immediate and predictable cleanup, while `finalize()` is delayed and unpredictable.
-
What are the alternatives to `finalize()`?
The preferred alternatives to `finalize()` are:
- Try-with-resources: For `AutoCloseable` resources.
- Explicit `close()` methods in `finally` blocks: For manual resource cleanup.
- Cleaners (Java 9+): For more controlled cleanup actions when an object becomes unreachable.