Is There a Destructor in Java? – 在Java中是否有一个破坏者?

最后修改: 2022年 6月 15日

1. Overview


In this short tutorial, we’ll look at the possibility of destroying objects in Java.

2. Destructor in Java


Every time we create an object, Java automatically allocates the memory on the heap. Similarly, whenever an object is no longer needed, the memory will automatically be deallocated.


In languages like C, when we finish using an object in memory, we have to deallocate it manually. Unfortunately, Java doesn’t support manual memory deallocation. Moreover, one of the features of the Java programming language is taking care of object destruction by itself – using a technique called garbage collection.


3. Garbage Collection


Garbage collection removes unused objects from memory on the heap. It helps prevent memory leaks. Simply put, when there are no more references to the specific object and the object is no longer accessible, the garbage collector marks this object as unreachable and reclaims its space.


Failure to handle garbage collection properly can cause performance problems, and eventually, it causes an application to run out of memory.


An object can be garbage collected when it reaches a state of no longer being accessible in the program. An object is no longer reachable when one of two situations occurs:


  • The object doesn’t have any references pointing to it
  • All references to the object have gone out of scope

Java includes the System.gc() method to help support garbage collection. By calling this method, we can suggest to the JVM to run the garbage collector. However, we cannot guarantee the JVM will actually call it. The JVM is free to ignore the request.


4. Finalizer


The Object class provides the finalize() method. Before the garbage collector removes an object from memory, it’ll call the finalize() method. The method can run zero or one time. However, it cannot run twice for the same object.


The finalize() method defined inside the Object class doesn’t perform any special action.


The main goal of the finalizer is to release resources used by the object before its removal from the memory. For instance, we can override the method to close the database connections or other resources.


Let’s create a class that contains the BufferedReader instance variable:


class Resource {

    final BufferedReader reader;

    public Resource(String filename) throws FileNotFoundException {
        reader = new BufferedReader(new FileReader(filename));

    public long getLineNumber() {
        return reader.lines().count();
In our example, we didn’t close our resources. We can close them inside the finalize() method:
protected void finalize() {
    try {
    } catch (IOException e) {
        // ...

When JVM calls the finalize() method, the BufferedReader resource will be released. The exceptions thrown by the finalize() method will stop the object finalization.


However, since Java 9, the finalize() method has become deprecated. Using finalize() method can be confusing and hard to use properly.

然而,自Java 9以来,finalize()方法已被废弃。使用finalize()方法会让人感到困惑,而且很难正确使用。

If we want to release resources held by an object, we should consider implementing the AutoCloseable interface instead. Classes like Cleaner and PhantomReference provide a more flexible way to manage resources once an object becomes unreachable.


4.1. Implementing AutoCloseable


The AutoCloseable interface provides the close() method, which will be executed automatically when exiting a try-with-resources block. Inside this method, we can close resources used by an object.


Let’s modify our example class to implement the AutoCloseable interface:


class Resource implements AutoCloseable {

    final BufferedReader reader;

    public Resource(String filename) throws FileNotFoundException {
        reader = new BufferedReader(new FileReader(filename));

    public long getLineNumber() {
        return reader.lines().count();

    public void close() throws Exception {

We can use the close() method to close our resources instead of using the finalize() method.


4.2. Cleaner Class


We can use the Cleaner class if we want to perform specific actions when an object becomes phantom reachable. In other words, when an object becomes finalized and its memory is ready to be deallocated.


Now, let’s see how to use the Cleaner class. Firstly, let’s define Cleaner:


Cleaner cleaner = Cleaner.create();

Next, we’ll create a class that contains a cleaner reference:


class Order implements AutoCloseable {

    private final Cleaner cleaner;

    public Order(Cleaner cleaner) {
        this.cleaner = cleaner;

Secondly, we’ll define a static inner class that implements Runnable inside the Order class:


static class CleaningAction implements Runnable {

    private final int id;

    public CleaningAction(int id) { = id;

    public void run() {
        System.out.printf("Object with id %s is garbage collected. %n", id);

Instances of our inner class will represent cleaning actions. We should register each cleaning action in order for them to run after an object becomes phantom reachable.


We should consider not using a lambda for the cleaning action. By using a lambda, we could easily capture the object reference, preventing an object from becoming phantom reachable. Using a static nested class, as above, will avoid keeping the object reference.


Let’s add the Cleanable instance variable inside the Order class:


private Cleaner.Cleanable cleanable;

The Cleanable instance represents the cleaning object that contains the cleaning action.


Next, let’s create a method that will register the cleaning action:


public void register(Product product, int id) {
    this.cleanable = cleaner.register(product, new CleaningAction(id));

Finally, let’s implement the close() method:


public void close() {

The clean() method unregisters the cleanable and invokes registered cleaning actions. This method will be called at most once regardless of the number of calls to clean.


When we use our CleaningExample instance inside a try-with-resources block, the close() method calls the cleaning action:


final Cleaner cleaner = Cleaner.create();
try (Order order = new Order(cleaner)) {
    for (int i = 0; i < 10; i++) {
        order.register(new Product(i), i);
} catch (Exception e) {
    System.err.println("Error: " + e);

In other cases, the cleaner will call the clean() method when an instance becomes phantom reachable.


Additionally, the behavior of cleaners during the System.exit() is implementation-specific. Java provides no guarantees whether cleaning actions will be invoked or not.


5. Conclusion


In this short tutorial, we looked at the possibility of object destruction in Java. To sum up, Java doesn’t support manual object destruction. However, we can use finalize() or Cleaner to free up the resources held by an object. As always, the source code for the examples is available over on GitHub.