Why finalizers are really bad

It is more or less common knowledge that using finalize functions in java is bad. For one, you are depending on garbage collection for cleanup and there is no guarantee when the finalizer will be called. Further there is also no guarantee it will ever get called even if the appliction terminates nicely using System.exit().

However there is a far more important reason why finalizers are bad. This is because objects with finalizers that become eligble for garbage collection are not collected immediately but, with the hotspot JVM, are first put onto a queue where a single finalizer thread calls the finalizers on each object in the queue in turn. This can cause a lot of problems:

  • a single object that hangs/blocks in its finalizer can block the whole queue and this cause a memory leak and bring your server application down
  • finalizers that do non-trivial work can take some time to run, so there is a risk here too that objects are created faster then they can be finalized, thus leading to the same memory leak.

To illistrate this, consider the following simple java code

public class Memleak {

    private static final AtomicInteger count = new AtomicInteger(0);

    public static final class LongFinalize {

        protected void finalize() throws Throwable {
            System.out.println("Finalizer " + getClass().getSimpleName());
            System.out.println("Blocking the queue for 5 seconds");
            Thread.sleep(5000);
            System.out.println("Finally finished the long finalization");
            super.finalize();
        }
    }

    public static final class ShortFinalize {
        public ShortFinalize() {
            count.incrementAndGet();
        }

        protected void finalize() throws Throwable {
            System.out.println("Finalizer " + getClass().getSimpleName());
            System.out.println(count.decrementAndGet() + " shortfinalize objects left");
            super.finalize();
        }
    }

    public static void main(String[] args) throws Exception {
        new LongFinalize();
        for (int i = 0; i < 100; i++) {
            new ShortFinalize();
        }
        // comment out the explicit call to gc to show that the finalizers will not get 
        // called at all. 
        System.gc();

        Thread.sleep(10000);

    }
}

In the above code there is one class that has finalizer that takes 5 seconds to execute. The other class has a fast finalizer. The fast finalizer also keeps track of a count of objects. The example creates one object with a long finalizer and 100 objects with short finalize methods where all these objects are immediately eligble for garbage collection. Running this example shows that when the long finalizer runs, no other finalizers can be run, confirming that there is indeed a single queue.

One case where I saw a memory leak occur was because of asynchronous logging in log4j where a finalizer of a log4j object was blocking on a Thread.join() operation where the thread it was waiting on never terminated (not really good design imho).

So, by any means, avoid finalize methods like the plague. Apart from the often mentioned issues with finalizers not getting called in time or at all, there is a great risk of memory leaks if you use finalizers.

This entry was posted in Java. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *