Reputation: 4331
They both can be used for cleanup, there is almost no guarantees, but PR requires more harness coding. So, having two options, why exactly I have to prefer one to another?
Javadoc 9 describes finalize as very problematic, but that doesn't make its alternative better automatically, right?
Also javadoc describes PhantomReference
as providing "more flexible and efficient ways to release resources when an object becomes unreachable", but without a reason specified. Well, I guess these guys know some secrets, but I'm wondering - can't be this choice made more obvious?
Here are all the differences between finalize (FZ) and pantom reference (PR) I discovered, please correct me if I missed something.
Can be used for cleanup actions?
Requires a new thread to maintain?
Requires a new class to define?
PhantomReference
to act meaningfullyCan cleanup processor access the referent object?
Does it work reliably in my personal practice?
Can lead to performance issues, deadlocks, and hangs?
Can errors in a cleanup processor lead to resource leaks?
Cancelable if it is no longer necessary?
return
that bad?Is invocation ordering between multiple instances specified?
Invocation guaranteed?
Any guarantees regarding the timing?
Can this
resurrect during processing?
Upvotes: 3
Views: 470
Reputation: 298469
Most of this has been addressed in Should Java 9 Cleaner be preferred to finalization? already. Since the Cleaner
API builds on the PhantomReference
, most of it applies as well to using PhantomReference
directly.
In short, you are not supposed to replace finalize()
usages with PhantomReference
or Cleaner
. For non-memory resources, you should prefer explicit closing right after using them, with the try-with-resources construct wherever feasible. The interaction with the garbage collector may act as a fallback to detect programming errors, but should not become the preferred way of resource cleanup.
In that regard, the ability to opt out the cleanup when the resource has been closed correctly, has significant relevance, as it will be the norm. You are underestimating the impact of it. Once, your class has a nontrivial finalizer, its objects require two garbage collection cycles, to get reclaimed, even when the finalize()
immediately returns after checking a condition. This can make the difference between getting collected in a minor gc or being promoted to the old generation.
The most extreme example would be a shortly used resource represented by a purely local object whose memory allocation could get elided completely after Escape Analysis has been applied, whereas the presence of a nontrivial finalize()
method invariably implies a global escape which prevents this optimization (amongst others, like lock elimination).
While the Cleaner
API does start a dedicated thread, there is no requirement to do so when using PhantomReference
. You could as well poll the queue right when the resource is used or a new one is about to be allocated. That doesn’t guaranty fast release of the resource (which gc triggered cleanup doesn’t guaranty anyway), but you’re ensuring that an allocation doesn’t fail while a collected object unnecessarily holds resources.
Even if you use dedicated threads for the cleanup, there’s a fundamental difference between starting threads under your control and having finalizers invoked by unspecified JVM threads outside your control, where a faulty finalize()
method of another library could block the thread needed for your cleanup. The JVM may invoke multiple finalizer concurrently whereas you can decide how many threads you use for your PhantomReference
based cleanup.
Upvotes: 4