Search code examples
javajava-9finalizerfinalizefinalization

Should Java 9 Cleaner be preferred to finalization?


In Java, overriding the finalize method gets a bad rap, although I don't understand why. Classes like FileInputStream use it to ensure close gets called, in both Java 8 and Java 10. Nevertheless, Java 9 introduced java.lang.ref.Cleaner which uses the PhantomReference mechanism instead of GC finalization. At first, I thought it was just a way add finalization to third-party classes. However, the example given in its javadoc shows a use-case that can easily be rewritten with a finalizer.

Should I be rewriting all of my finalize methods in terms of Cleaner? (I don't have many, of course. Just some classes that use OS resources particularly for CUDA interop.)

As I can tell, Cleaner (via PhantomReference) avoids some of the dangers of finalizer. In particular, you don't have any access to the cleaned object and so you can't resurrect it or any of its fields.

However, that is the only advantage I can see. Cleaner is also non-trivial. In fact, it and finalization both use a ReferenceQueue! (Don't you just love how easy it is to read the JDK?) Is it faster than finalization? Does it avoid waiting for two GCs? Will it avoid heap exhaustion if many objects are queued for cleanup? (The answer to all of those would appear to me to be no.)

Finally, there's actually nothing guaranteeing to stop you from referencing the target object in the cleaning action. Be careful to read the long API Note! If you do end up referencing the object, the whole mechanism will silently break, unlike finalization which always tries to limp along. Finally, while the finalization thread is managed by the JVM, creating and holding Cleaner threads is your own responsibility.


Solution

  • Use neither.

    Trying to recover from resource leaks using Cleaner presents nearly as many challenges as finalize the worst of which, as mentioned by Holger, is premature finalization (which is a problem not only with finalize but with every kind of soft/weak/phantom reference). Even if you do your best to implement finalization correctly (and, again, I mean any kind of system that uses a soft/weak/phantom reference), you can never guarantee that the resource leaks won't lead to resource exhaustion. The unavoidable fact is that the GC doesn't know about your resources.

    Instead, you should assume that resources will be closed correctly (via AutoCloseable, try-with-resources, reference counting, etc.), find and fix bugs rather than hope to work around them, and use finalization (in any of its forms) only as a debugging aid, much like assert.

    Resource leaks must be fixed--not worked around.

    Finalization should only be used as an assertion mechanism to (try to) notify you that a bug exists. To that end, I suggest taking a look at the Netty-derived almson-refcount. It offers an efficient resource leak detector based on weak references, and an optional reference-counting facility that is more flexible than the usual AutoCloseable. What makes its leak detector great is that it offers different levels of tracking (with different amounts of overhead) and you can use it to capture stack traces of where your leaked objects are allocated and used.