Adrian D. Finlay

@afinlay

java.lang.Object.finalize() is finally deprecated

GIPHY

And thus, finalize(), having been fed up with his own unreliability, walked out of Object’s boardroom, never to be seen again, or so we would like to believe.

Although many may not have realized, with the JDK 9 release came the deprecation of finalize(). Don’t believe me, check Java 9’s documentation! The reader be aware that deprecation does not necessarily mean removal or future removal. It is an indication used to note that the annotated element should (if possible) be avoided and may potentially be removed in future releases of the Java platform.

At the moment, it is not certain whether or not finalize() will be marked for removal. I personally don’t believe that it will be removed, but I am not sure. All I can is that Early Access Builds of JDK 11 have not removed it and as it is a method that is inherited by every single class in the Java ecosystem, my gut is nudging me to believe it’s going to stay put.

Nevertheless, if the metadata is any indication of what the API writer’s intentions are, at least at the time of writing, it seems as if it is not going to be removed —at least not in the immediate future. We can induce this by running and analyzing the result of this code:

/* Note that this uses language features only available in Java 10 and 11, namely, local variable type inference. The use of this feature within Lambda Expression arguments came about in Java 11 and is used in this article. Java 10 is set to be released 6 days following the publishing of this article. You may learn more about local variable type reference @ bit.ly/VarJava */
import java.lang.annotation.Annotation;
...
var annos = Object.class.getDeclaredMethod("finalize", (Class<?>[]) null ) .getAnnotations();
for (var anno : annos) System.out.println(anno + "\n");
Output: @java.lang.Deprecated(forRemoval=false, since="9")

We ran this code for fairly obvious reasons, which is to show, programmatically, that java.lang.Object.finalize() has not been marked for removal.

The deprecation of finalize() can be somewhat considered as ceremonial icing on a long-time proverbial cake.

Dating all the way back to 2001, in the first edition of his blockbuster classic “Effective Java” (which ought to be compulsory reading for serious Java developers), Joshua Bloch warned about the dangers of relying on finalize().

Sun (and thereafter, Oracle) has long cautioned Java developers about making use of Finalization and why it is almost (if not outright) always a bad idea. A primary motivation in developing finalize() was to be able to dispose of “unreachable objects” in code, such as native resources.

In the past, one might have been tempted to use finalize() as follows.

Some notes on my environment: I’ll be using an early access build of JDK 11 on Linux Mint MATE.

Can finalize() reliably close your stream?

Let’s write a telephone book modeled with the MVC Architectural pattern.

First, the Model, a POJO bean called Contact.

Second, the Controller, a class called TelephoneBook.

Third, the View, our main class, called Finalize.

When run, this is the output.

Analysis.

Take a moment and look over the code. The main thing to grasp here is that we are relying on finalize() to do some of our clean up: closing the java.io.PrintWriter stream. Now draw your attention to lines 16 and 17 of the Finalize class. Note that we both set an instance of TelephoneBook to null as well as meekly suggested to the Garbage Collector that now might be a good time to run. Recall that System.gc() is not a direct trigger of the garbage collector and finalize() is not a destructor. Neither are required to run. At the heart of all this is the fact that that Garbage Collector will run when it pleases and we can’t (for our practical purposes) control when it does.

Did you notice that the Garbage Collector ran and that finalize() was called in our example? Try this experiment. Remove either line 16 or line 17 of the Finalize class and see if the finalize() method was called. I’m willing to be it didn’t, although I can’t be sure. For me, it repeatedly did not. Remember, we cannot (in most practical cases at least) deterministically control when the Garbage Collector is run.

Thus, if we rely on finalize() to close our OutputStream, we are engaging in sloppy programming.

Do you see why finalize() is a bad idea? This is just one example of many but the message is clear: because we can’t guarantee that finalize() will be called or determine when the GC will be run, we can’t reliably use finalize() to finish closing tasks, such as closing I/O streams. What if we had to close an SSLSocket? Or a JDBC Connection? Or a JNI resource? I’ll leave it to the reader’s imagination (or nightmares) to muse about the mayhem that could be caused.

Closing Thoughts

Java 9 is the long awaited ceremonializing an old truism: you can’t rely on finalize() to clean up after you. Behave, and clean up your resources explicitly and by other means. As also hinted to, there are other issues with finalize(), namely: performance, deadlocks, and hangs[1].

To understand why System.gc() is bad practice in more depth, visit here:

Despise finalize() like the rest of us? Or do you think it’s still a good idea? Let me know in the comments below :)

Want the source? Grab it here.

You will need JDK 11 which you can find, here.

Goodbye, finalize(). You will not be missed.

Interested in Java? Join my Java group on Facebook:

Like my Content? Subscribe to my mailing list:

Don’t forget to give it a…. ;)

IEmoji.com

Works Cited

[1] —JDK 9 API Documentation: java.lang.Object.finalize()

More by Adrian D. Finlay

Topics of interest

More Related Stories