Java finalize() – Why We Should Not Use It?

The finalize() method in Java is called by garbage collector thread before reclaiming the memory allocated to the object. It is considered as a destructor in Java – though It is not similar to what we have in C/C++.

Let’s learn when to use finalize() in Java – and why we should avoid using it, if possible.

The JEP-421 (Java 18) has marked the finalization deprecated. It will be removed in a future release. The use of Cleaners is recommended.

1. The finalize() in Java

1.1. Syntax

It is called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize() method to dispose of system resources or to perform other cleanups.

Notice that finalize() is the deprecated method.

@Deprecated(since="9")
protected void finalize() throws Throwable

1.2. Implementing finalize()

A general syntax to override finalize() method is given below.

public class MyClass 
{
 
  //Other fields and methods
 
  //...
   
  @Override
  protected void finalize() throws Throwable
  {
    try
    {
      //release resources here
    } 
    catch(Throwable t) 
    {
      throw t;
    } 
    finally
    {
      super.finalize();
    }
  }
}

2. The finalize() Execution is Not Guaranteed

Let’s prove it using a program. I have written a simple Java runnable with one print statement in each block i.e. try-catch-finally-finalize.

I have also created another class that will create 3 instances of this runnable and then we will see the execution path.

public class TryCatchFinallyTest implements Runnable {
 
  private void testMethod() throws InterruptedException
  {
    try
    {
      System.out.println("In try block");
      throw new NullPointerException();
    }
    catch(NullPointerException npe)
    {
      System.out.println("In catch block");
    }
    finally
    {
      System.out.println("In finally block");
    }
  }
 
  @Override
  protected void finalize() throws Throwable {
    System.out.println("In finalize block");
    super.finalize();
  }
 
  @Override
  public void run() {
    try {
      testMethod();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
public class TestMain
{
  @SuppressWarnings("deprecation")
  public static void main(String[] args) {
  for(int i=1;i<=3;i++)
  {
    new Thread(new TryCatchFinallyTest()).start();
  }
  }
}

Program output.

In try block
In catch block
In finally block
 
In try block
In catch block
In finally block
 
In try block
In catch block
In finally block

Amazing, finalize() method has not been executed at all for any thread. So this proves what I stated already.

The reason which I can think of is – that finalizers are executed by a separate thread from the garbage collector. If JVM exits too early then the garbage collector does not get enough time to create and execute finalizers.

2.1. Can we force finalize() to execute?

The answer is yes. Yes, we can. Using Runtime.runFinalizersOnExit(true);

public class TestMain
{
  @SuppressWarnings("deprecation")
  public static void main(String[] args) {
    for(int i=1;i<=3;i++)
    {
      new Thread(new TryCatchFinallyTest()).start();
      Runtime.runFinalizersOnExit(true);
    }
  }
}

Program output.

In try block
In catch block
In finally block
 
In try block
In try block
In catch block
In finally block
In catch block
In finally block
 
In finalize block
In finalize block
In finalize block

There is another method also: Runtime.getRuntime().runFinalization(); but, it only guarantees that GC will make its best efforts. Even our program is not able to run finalize() method for all 3 threads.

Moving forward, we have used Runtime.runFinalizersOnExit(true). Well, this is another pit. This method has already been deprecated in JDK, for the following reason –

“This method is inherently unsafe. It may result in finalizers being called on live objects while other threads are concurrently manipulating those objects, resulting in erratic behavior or deadlock.”

So, in one way we can not guarantee the execution and in another way we the system in danger. Better, don’t use this method.

3. Other Reasons for Not Using finalize()

  1. finalize() methods do not work in chaining like constructors. It means when you call a constructor then constructors of all superclasses will be invoked implicitly. But, in the case of finalize() methods, this is not followed. Ideally, parent class’s finalize() should be called explicitly but it does not happen.
  2. Suppose, you created a class and wrote its finalize method with care. Someone comes and extends your class and does not call super.finalize() in subclass’s finalize() block, then super class’s finalize() will never be invoked anyhow.
  3. Any exception which is thrown by finalize() method is ignored by GC thread and it will not be propagated further, in fact it will not be logged in your log files. So bad, isn’t it?

4. finalize() adds Heavy Penalty on Performance

In Effective Java (2nd edition) Joshua Bloch says:

“Oh, and one more thing: there is a severe performance penalty for using finalizers. On my machine, the time to create and destroy a simple object is about 5.6 ns. Adding a finalizer increases the time to 2,400 ns. In other words, it is about 430 times slower to create and destroy objects with finalizers.”

I also tried the above analysis on my system but I was not able to get that much difference. Although, there is some difference for sure. But that was around 30% on the original time. On time-critical systems, it is also a big difference.

5. How to Use finalize() Correctly

After all the above arguments, if you still find a situation where using finalize() is essential, then cross-check below points:

  • Always call super.finalize() in your finalize() method.
  • Do not put time-critical application logic in finalize(), seeing its unpredictability.
  • Do not use Runtime.runFinalizersOnExit(true); as it can put your system in danger.
  • Try to follow the below template for finalize() method
@Override
protected void finalize() throws Throwable
{
  try{
    //release resources here
  }catch(Throwable t){
    throw t;
  }finally{
    super.finalize();
  }
}

In this post, we discussed Java finalize best practice, how can we call finalize method manually in Java, and why not to use finalize in Java applications.

Happy Learning !!

Comments

Subscribe
Notify of
guest
29 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode