Avoid finalizers
Finalizers are unpredictable, often dangerous, and generally unnecessary.
-
1.As a rule of thumb, you should avoid finalizers
- do not to think of finalizers as Java’s analog of C++ destructors.
- [In C++] destructors are the normal way to reclaim the resources associated with an object, a necessary counterpart to constructors. it can reclaim other nonmemory resources.
- [In Java] the garbage collector reclaims the storage associated with an object when it becomes unreachable, requiring no special effort on the part of the programmer.
- do not to think of finalizers as Java’s analog of C++ destructors.
-
2.One shortcoming of finalizers
- [1] there is no guarantee finalizers will be executed promptly
- [arbitrarily long] the time that an object becomes unreachable and the time that its finalizer is executed
- never do anything time-critical in a finalizer.
- e.g., [grave error] depend on a finalizer to close files
- 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.
- [no guarantees] no guarantee that finalizers will get executed promptly; no guarantee that they’ll get executed at all
- [low priority] finalizers thread may run at a lower priority
- e.g., an application may have thousands of graphics objects on its finalizer queue just waiting to be finalized and reclaimed. Finalizers thread may run at a lower priority
- [2] no warning.
- if an uncaught exception is thrown during finalization, the exception is ignored
- 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.
- [3] performance penalty
- 430 times slower
- [Conclusion] never depend on a finalizer to update critical persistent state.
- e.g., 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.
- [1] there is no guarantee finalizers will be executed promptly
-
3.Don’t
- Don’t use System.gc, System.runFinalization (may increase the odds of finalizers getting executed, but no guarantee)
- Don’t use System.runFinalizersOnExit and Runtime.runFinalizersOnExit (Deprecated; ThreadStop)
So what should you do instead of writing a finalizer for a class whose objects encapsulate resources that require termination, such as files or threads?
-
4.What should you do?
- Just provide an explicit termination method
- [details] every 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 an IllegalStateException if they are called after the object has been terminated.
- [details] every instance must keep track of whether it has been terminated:
-
Explicit termination methods are typically used in combination with the try-finally construct to ensure termination.
// 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 }
- Typical examples
- The close methods on InputStream , OutputStream , and java.sql.Connection .
- The cancel method on java.util.Timer (cause the thread associated with a Timer instance to terminate itself gently)
- Graphics.dispose and Window.dispose on java.awt. (Image.flush: deallocates all the resources but the instance can still be used)
- Just provide an explicit termination method
-
5.what, if anything, are finalizers good for?(Two)
- [1]”safety net” finalizer
- in case the owner of an object forgets to call its explicit termination method.
- it may be better to free the resource late than never.
- the finalizer should log a warning if it finds that the resource has not been terminated (indicates a bug in the client code)
- FileInputStream , FileOutputStream , Timer , and Connection have safe nets but do not log warnings, in case of break existing clients.
- [?] whether the extra protection is worth the extra cost ?
- [2] native peers
- A native peer is a native object to which a normal object delegates via native methods.
- A native peer is not a normal object; GC doesn’t know it and can’t reclaim it.
- The native should have an explicit termination method (can be a native method)
- A finalizer is an appropriate vehicle for performing this task
- [1]”safety net” finalizer
-
6.finalizer chaining
- If a class (other than Object ) has a finalizer and a subclass overrides it
- the subclass finalizer must invoke the superclass finalizer manually.
-
using finally block to ensures that the superclass finalizer gets executed
// Manual finalizer chaining @Override protected void finalize() throws Throwable { try { ... // Finalize subclass state } finally { super.finalize(); } }
-
7. using anonymous class (Item 22) to defend against careless or malicious subclasses
- A single instance of the anonymous class, is created for each instance of the enclosing class.
- it doesn’t matter whether a subclass finalizer calls super.finalize or not.
-
The technique should be considered for every nonfinal public class that has a finalizer.
// 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 }
-
Conclusion:
- don’t use finalizers except as a safety net(remember to log the invalid usage) or to terminate noncritical native resources.
- for rare instances where you do use a finalizer, remember to invoke super.finalize
- consider using a finalizer guardian (if you need to associate a finalizer with a public, nonfinal class)