Effective Java! Avoid Finalizers and Cleaners!

Kyle Carter
4 min readDec 26, 2020

Here we are at the eighth chapter of Effective Java and it’s another more niche topic. Today we are talking about finalizers and cleaners. Huh? What are those? The author of Effective Java just kind of jumps into why not to use them and doesn’t really explain what they are there for (maybe because he doesn’t want to entice you to use them). Anyway, I figured it would be good to actually start with what they are before we dig into why we shouldn’t use them because when I first read about them I had never heard of or used them.

The two concepts basically do the same thing but in two different ways. Finalizers are the elder of the two options and is a core part of the Java language. It is a method on the class Object that can be overridden. So what does it do. Simply a finalizer is a method that gets called right before a object is removed by the garbage collector. This function can be used for many purposes including bringing the object back to life (why? 🤷) but most commonly used to clean up resources for an object. Cleaners are more or less the same type of thing (but not part of the class Object) but for a post Java 9 world where finalizers were deprecated and help overcome some of the shortcomings. They however still should be avoided.

OK, now that we know what they do and how they can be useful let’s ruin it by saying all the reasons we shouldn’t use them.

The Time When the Finalizer Is Indeterminate
Because we don’t have control over when the garbage collector runs we also cannot be sure when the finalizer will run. Because of this we shouldn’t count on it cleaning up finite resources like file handles for us as it may not be able to do it in time. While we likely will get some consistency of behavior if we test on the same JVM we deploy on but there will still be plenty of variability. If we deploy on different JVMs this becomes even more wide open to different behaviors. We also run the risk of the finalizer thread being run at a lower priority than the rest of the threads in the system and thus no being able to keep up with the creation of objects.

The Finalizer May Never Be Called
So what is worse than not being able to determine when the finalizer will run is realizing that the finalizer may never run. That’s right, according to the specification a finalizer may never be called. This should make it apparent that these should not be used to clean up persistent state. There are methods that can…



Kyle Carter

I'm a software architect that has a passion for software design and sharing with those around me.