Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems.
One shortcoming of finalizers is that there is no guarantee they’ll be executed promptly [JLS, 12.6]. It can take arbitrarily long between the time an object becomes unreachable and the time that its finalizer is executed. This means you should never do anything time-critical in finalizers.
There is severe performance penalty of using finalizers. On my machine, the time to create and destroy a simple object is about 5.6ns. Adding a finalizer increases the time to 2,400ns. In other words, it is about 430 times slower to create and destroy objects with finalizers.
What should you do instead of writing finalizer for a class whose objects encapsulate resources that require termination, such as files or threads?
Just provide an explicit termination method, and require clients of the class to invoke this method on each instance when it is no longer needed. One detail worth mentioning is that the instance must keep track of whether it has been terminated: the explicit termination method must record in a private field that the object is no longer valid, and other methods must check this field and throw IllegalStateException if there are called after the object has been terminated.
Typical examples of explicit termination methods are the close methods on InputStream, OutputStream and java.sql.Connection. Another example is the cancel method on java.util.Timer, which performs the necessary state change to cause thread associated with a Timer instance to terminate itself gently.
Explicit termination methods are typically used in combination with the try-finally construtor to ensure termination.
Foo foo = new Foo(...); try { .... } finally { foo.terminate(); }
Finalizers should log a warning if it finds that the resource has not been terminated.
It is important to note that “finalizers chaining” is not performed automatically.
// Manual finalizer chaining @Override protected void finalize() throws Throwable { try { .. // Finalize subclass state } finally { super.finalize(); } }If a subclass implementor overrides a superclass finalizer but forgets to invoke it, the superclass finalizer will never be invoked.
Instead of putting the finalizer on the class requiring finalization, put the finalizer on an anonymous class whose sole purpose os to finalize the enclosing instance.
// Finalizer Guardian idiom public class Foo { // Sole purpose of this object is to finalize outer Foo object private final Object finalizerGuardian = new Object() { @Override protected void finalize() throws Throwable { ... // Finalize outer Foo object } } ... }