The first segment of Episode 23 of the Java Off-Heap podcast covered the deprecation of Object.finalize
in Java 9 and deprecation and finalization in general. Deprecation is a subject near and dear to my heart. The hosts even mentioned me by name. Thanks for the shout-out, guys!
I wanted to clarify a few points and to answer some of the questions that weren’t resolved in that segment of the show.
Java Finalizers vs. C++ Destructors
The role of Java’s finalizers differs from C++ destructors. In C++ (prior to the introduction of mechanisms like shared_ptr
) anytime you created something with new
in a constructor, you were required to call delete
on it in the destructor. People mistakenly carried this thinking over to Java, and they thought that it was necessary to write finalize
methods to null out references to other objects. (This was never necessary, and the fortunately the practice seems to have died out long ago.) In Java, the garbage collector cleans up anything that resides on the heap, so it’s rarely necessary to write a finalizer.
Finalizers are useful if an object creates resources that aren’t managed by the garbage collector. Examples of this are things like file descriptors or natively allocated (“off-heap”) memory. The garbage collector doesn’t clean these up, so something else has to. In the early days of Java, finalization was the only mechanism available for cleaning up non-heap resources.
Phantom References
The point of finalization is that it allows one last chance at cleanup after an object becomes unreachable, but before it’s actually collected. One of the problems with finalization is that it allows “resurrection” of an object. When an object’s finalize
method is called, it has a reference to this
— the object about to be collected. It can hook the this
reference back into the object graph, preventing the object from being collected. As a result, the object can’t simply be collected after the finalize
method returns. Instead, the garbage collector must run again in order to determine whether the object is truly unreachable and can therefore be collected.
The reference package java.lang.ref
was introduced all the way back in JDK 1.2. This package includes several different reference types, including PhantomReference
. The salient feature of PhantomReference
is that it doesn’t allow the object to be “resurrected.” It does this by making the contained reference be inaccessible. A holder of a phantom reference gets notified that the referent has become unreachable (strictly speaking, phantom-reachable) but there’s no way to get the referent out and hook it back into the object graph. This makes the garbage collector’s job easier.
Another advantage of a PhantomReference
is that, like the other reference types, it can be cleared explicitly. Suppose there’s an object that holds some external resource like a file descriptor. Typically, such objects have a close
method the application should call in order to release the descriptor. Prior to the introduction of the reference types, such objects also need a finalize
method in order to clean up if the application had failed to call close
. The problem is, even if the application has called close
, the collector needs to do finalization processing and then run again, as described above, in order to collect the object.
PhantomReference
and the other reference types have a clear
method that explicitly clears the contained reference. An object that has released its native resources via an explicit call to a close
method would call PhantomReference.clear
. This avoids a subsequent reference processing step, allowing the object to be collected immediately when it becomes unreachable.
Why Deprecate Object.finalize Now?
A couple of things have changed. First, JEP 277 has clarified the meaning of deprecation in Java 9 so that it doesn’t imply that the API will be removed unless forRemoval=true
is specified. The deprecation of Object.finalize
is an “ordinary” deprecation in that it’s not being deprecated for removal. (At least, not yet.)
A second thing that’s changed in Java 9 is the introduction of a class java.lang.ref.Cleaner
. Reference processing is often fairly subtle, and there’s a lot of work to be done to create a reference queue and a thread to process references from that queue. Cleaner
is basically a wrapper around ReferenceQueue
and PhantomReference
that make reference handling easier.
What hasn’t changed is that for years, it’s been part of Java lore that using finalization is discouraged. It’s time to make a formal declaration, and the way to do this is to deprecate it.
Has Anything Ever Been Removed from Java SE?
The podcast episode mentioned a Quora answer by Cameron Purdy written in 2014, where he said that nothing had ever been removed from Java. When he wrote it, the statement was correct. Various features of the JDK had been removed (such as apt, the annotation processing tool), but public APIs had never been removed.
However, the following six APIs were deprecated in Java SE 8, and they have been removed from Java SE 9:
java.util.jar.Pack200.Packer.addPropertyChangeListener
java.util.jar.Pack200.Unpacker.addPropertyChangeListener
java.util.logging.LogManager.addPropertyChangeListener
java.util.jar.Pack200.Packer.removePropertyChangeListener
java.util.jar.Pack200.Unpacker.removePropertyChangeListener
java.util.logging.LogManager.removePropertyChangeListener
In addition, in Java SE 9, about 20 methods and six modules have been deprecated with forRemoval=true
, indicating our intent to remove them from the next major Java SE release. Some of the classes and methods to be removed include:
java.lang.Compiler
Thread.destroy
System.runFinalizersOnExit
Thread.stop(Throwable)
The modules deprecated for removal are the following:
java.activation
java.corba
java.transaction
java.xml.bind
java.xml.ws
java.xml.ws.annotation
So yes, we are getting serious about removing stuff!
Will Finalization Be Removed?
As mentioned earlier, Object.finalize
is not being deprecated for removal at this time. As such, its deprecation is merely a recommendation that developers consider migrating to alternative cleanup mechanisms. The recommended replacements are PhantomReference
and the new Cleaner
class.
That said, we do eventually want to get rid of finalization. It adds extra complexity to the garbage collector, and there are recurring cases where it causes performance problems.
Before we can get rid of it, though, we need to remove uses of it from the JDK. That’s more than just removing the overrides of finalize
and rewriting the code to use Cleaner
instead. The problem is that there are some public API classes in the JDK that override finalize
and specify its behavior. In turn, their subclasses might override finalize
and rely on the existing behavior of super.finalize()
. Removing the finalize
method would expose these subclasses to a potentially incompatible behavior change. This will need to be investigated carefully.
There might also be a transition period where calling of the finalize
method is controlled by a command-line option. This would allow testing of applications to see if they can cope without finalization. Only after a transition period would we consider removing the finalization mechanism entirely. We might even leave the finalize
method declaration around for binary compatibility purposes, even after the mechanism for calling it has been removed.
As you can see, removing finalization would require a long transition period spanning several JDK releases, taking several years. That’s all the more reason to start with deprecation now.
[…] Using the JVM? (I sure hope so!) You might care about the discussion of the deprecation of java.lang.Object#finalize […]
Thanks for the post Stuart. An interesting & related article on how finalize is being incorrectly used by FileInputStream & FileOutputStream & causing long GC pauses – https://dzone.com/articles/fileinputstream-fileoutputstream-considered-harmful
Yes, that article is quite relevant. It’s not that FileInputStream and FileOutputStream use finalization incorrectly, however. They override finalize in order to ensure the file descriptor is closed. This means the JVM has to finalize the object even if the application has already closed it explicitly. FIS & FOS are examples that illustrate the disadvantage of finalizers as compared to PhantomReference. Finalization is not cancelable, whereas reference process is cancelable.
I agree. By incorrectly I meant they should have used some other means to close the file descriptor instead ( probably PhantomReference as you suggest ) but since its a part of their public API now, it’s pretty hard to change that.
[…] >> Deprecation of Object.finalize() [stuartmarks.com] […]
What if a Singleton class which need a DB Connection for its most of its method calls? This class may be called by the classes which reside in a layer, completely unaware of DB being used backend. Things can be designed to avoid finalize method. But checking if connection is isAlive in finalize method, to make sure connection gets closed before application shutdown, makes application better. Probably finalize method has not been borrowed from C++, but from OOPS. Forgetting to clean the things created during Constructor, will increase the garbage when Garbage Collector unaware of the garbage that needs to collect. Good developer always thinks about what needs to put in finalize method when he writes Constructor. If people are not throwing garbage neatly, doesn’t mean we don’t need Dustbins..
It’s clearly necessary for some objects to clean up after themselves just prior to garbage collection. The finalization mechanism was intended to handle that, but it’s flawed. The preferred approach for pre-GC cleanup is to use reference processing (e.g., Cleaner or PhantomReference) instead of finalization.
We find lots of non-user-friendly ness when we go through hard core OOPS. but practical thinking of OOPS always gives better structure. We I have something waste in our house, we just throw those in dustbin, with caring when that dust bin gets cleared. Very important thing is, garbage collector realy cleans it.. finalize suites well with OOPS, but cleaners can always be a utility. Finalize could be there with out discouragement like ‘deprecated’. When I have something uncommon(non-generic(db connection/file stream) things in my programme which garbage collector not aware how to clean, but I need to tell him when he cleans it with a way to specialized cleaning. Cleaning is not my job, Java promised us, it will take care it, I have to tell garbage collector that “THIS GARBASE NEEDS TO BE HANDLED ‘THIS WAY’ “
[…] Deprecation of Object.finalize() […]
[…] a recent blog post, entitled Deprecation of Object.finalize(), Oracle Technical Staff Principal Stuart Marks set the record straight not only on what was being […]