After my previous post on references one of the team asked me for an example use for phantom references. The best I could recall was to keep track of an object and perform some postmortem clean upon its destruction. Very important for database connections and caches.
But can’t you do that with the finalize method?
Well no, not with any guarantees. Here’s why:
Firstly, we don’t know when the garbage collector will run. It is non-deterministic. In fact, if your program doesn’t use up its allocated memory it doesn’t ever need to GC — no objects would befinalized. Secondly, any objects that are strongly referenced when the runtime quits will not be GC’d. Their finalizer code will never be called.
Object finalization happens after an object has been marked as garbage but before memory has been reclaimed. You can screw around with this by creating a strong reference to a garbage object during its finalization:
myObject = this;
If you do this (please don’t!) the object comes back to life because it is strongly reachable via
myObject. However, the object you have a reference to will never have its finalizer code called again. That’s bad. Phantom references don’t allow this:
“In order to ensure that a reclaimable object remains so, the referent of a phantom reference may not be retrieved: The get method of a phantom reference always returns
Recall that the constructor for a PhantomReference takes a ReferenceQueue. Phantom references are enqueued once the object has been finalized and the memory has been reclaimed. If you process this queue in your own cleanup thread then you are guaranteed that the referents are gone.
“Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued“
[PhantomReference API] – So you just remember to clean up and dequeue.
If that’s not enough to convince you that finalizers are troublesome then maybe an optimization argument will help: the runtime’s finalizer thread is responsible for calling the finalize method on garbage objects. This can slow up your application during GC cycles. Especially if your finalizer code does a synchronous wait for some external process (like a database confirming the connection was closed). Taking control of cleanup with your own thread(s) by processing a reference queue is likely to be way more efficient.
Finalizerly, if the garbage collector never runs then unreachable objects are not identified which means phantom references will not be put into the reference queue. That’s important to remember too.