Finalizers are
unpredictable, often dangerous, and generally unnecessary. Their use can
cause erratic behavior, poor performance, and portability problems.
You should never do
anything time-critical in a finalizer. For example, it is a grave error
to depend on a finalizer to close files, because open file descriptors are a
limited resource. If many files are left open because the JVM is tardy in
executing finalizers, a program may fail because it can no longer open files.
You should never depend on
a finalizer to update critical persistent state. For example,
depending on a finalizer to release a persistent lock on a shared resource such
as a database is a good way to bring your entire distributed system to a grinding
halt.
Don’t be
seduced by the methods System.gc and System.runFinalization. They may increase the odds of
finalizers getting executed, but they don’t guarantee it. The only methods that
claim to guarantee finalization are System.runFinalizersOnExit
and
its evil twin, Runtime.runFinalizersOnExit. These
methods are fatally flawed and have been deprecated [ThreadStop].
If an uncaught
exception is thrown during finalization, the exception is ignored, and
finalization of that object terminates [JLS, 12.6]. Uncaught exceptions can
leave objects in a corrupt state. If another thread attempts to use such a
corrupted object, arbitrary nondeterministic behavior may
result.
Normally, an uncaught exception will terminate the thread and print a stack trace,
but not if it occurs in a finalizer—it won’t even print a warning.
There is a severe performance penalty for using finalizers. On my machine,
the time to create and destroy a simple object is about 5.6 ns. Adding a
finalizer increases the time to 2,400 ns. In other words, it is about 430 times
slower to create and destroy objects with finalizers.
What should
you do instead of writing a 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. 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.
Explicit
termination methods are typically used in combination with the try-finally construct to ensure termination. Invoking the
explicit termination method inside the finally
clause
ensures that it will get executed even if an exception is thrown while the
object is being
used:
// try-finally block guarantees execution of
termination method
Foo foo = new Foo(...);
try {
// Do what must be done with foo
...
} finally {
foo.terminate(); // Explicit termination method
}
The finalizer
should log a warning if it finds that the resource has not been terminated, as this
indicates a bug in the client code, which should be fixed.
It is
important to note that “finalizer chaining” is not performed automatically. If
a class (other than Object) has a
finalizer and a subclass overrides it, the subclass finalizer must invoke the
superclass finalizer manually. You should finalize the subclass in a try block and invoke the superclass finalizer in
the corresponding finally block. This
ensures that the superclass finalizer gets executed even if the subclass
finalization throws an exception and vice versa.
// 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. It is possible to defend against
such a careless or malicious subclass at the cost of creating an additional
object for every object to be finalized. Instead of putting the finalizer on
the class equiring finalization, put the finalizer on an anonymous class (Item 22) whose sole purpose is to finalize its enclosing
instance. A single instance of the
anonymous
class, called a finalizer guardian, is created
for each instance of the enclosing class.
// 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
}
};
... // Remainder omitted
}
Note that the
public class, Foo, has no finalizer (other than
the trivial one it inherits from Object), so it
doesn’t matter whether a subclass finalizer calls super.finalize or not. This technique should be considered
for every nonfinal public class that has a finalizer.
In summary,
don’t use finalizers except as a safety net or to terminate noncritical native
resources. In those rare instances where you do use a finalizer, remember to
invoke super.finalize. If you use a
finalizer as a safety net, remember to log the invalid usage from the
finalizer. Lastly, if you need to associate a finalizer with a public, nonfinal
class, consider using a finalizer guardian, so finalization can take place even
if a subclass finalizer fails to invoke super.finalize.
Reference: Effective Java 2nd Edition by Joshua Bloch