Sponsored Link •
When to use finalizers
In my Design Techniques article, I concluded that finalizers are useful only rarely, but I gave a few examples of when finalizers did make sense:
One reasonable, though rare, application for a finalizer is to free memory allocated by native methods. If an object invokes a native method that allocates memory (perhaps a C function that calls
malloc()), that object's finalizer could invoke a native method that frees that memory (calls
free()). In this situation, you would be using the finalizer to free up memory allocated on behalf of an object -- memory that will not be automatically reclaimed by the garbage collector.
Another, more common, use of finalizers is to provide a fallback mechanism for releasing finite nonmemory resources such as file handles or sockets. As mentioned previously, you shouldn't rely on finalizers for releasing finite nonmemory resources. Instead, you should provide a method that will release the resource. But you may also wish to include a finalizer that checks to make sure the resource has already been released, and if it hasn't, that goes ahead and releases it. Such a finalizer guards against (and hopefully will not encourage) sloppy use of your class. If a client programmer forgets to invoke the method you provided to release the resource, the finalizer will release the resource if the object is ever garbage collected. The
finalize()method of the
LogFileManagerclass, shown later in this article, is an example of this kind of finalizer.
The "Finalizable Object" idiom
After ending my Design Techniques column in early 1999, I began to organize the column material into a book called The Precise Object. This book, which I am still in the process of writing, will include 10 Java object idioms. In organizing the material, I decided that using a finalizer to perform the cleanup neglected by the client code was important enough to mention as an idiom in the book. I call this idiom the "Finalizable Object" idiom.
Any class containing one or more methods that can grab a finite nonmemory resource, which isn't released before the method returns, should be finalizable. Here's the recipe for the Finalizable Object idiom:
close()method, that client programmers should invoke before releasing the last reference to the object. Describe the cleanup requirements in the documentation for the type in which the cleanup method(s) are declared. (It might be multiple methods, for example, because if an object fires off a thread and opens a file, the client might need to invoke
stop()to stop the thread and
close()to close the file.)
Testing "final conditions"
On a few occasions I've presented The Precise Object material in a workshop with Bruce Eckel. In the workshop, which I call the "Object Design Workshop," I present a guideline or idiom, then lead a discussion about it, so that participants can share their experiences and opinions and learn from one another.
Last summer, Bruce and I gave our first iteration of this workshop in Crested Butte, Colorado. During the
Finalizable Object idiom discussion, Bruce expressed concern that if someone defines a finalizer that does
clean up, then client programmers might become lazy about calling the cleanup method. He was worried that
programmers would see the finalizer and think, "I don't need to invoke
close() (or whatever
the cleanup method is called), because the finalizer will invoke
close() for me." Such
laziness would be dangerous because the program would depend on timely finalization for correctness. Given
such a dependency, the application could potentially run out of file handles on some virtual machine implementations.
Bruce and I realized that such a finalizer could detect a programming bug, which I subsequently started calling an unmet "final condition." The semantics of a finalizable object should state that clients must put the finalizable object into a "cleaned up" state, by invoking the cleanup method or methods on the object, before the client releases all references to the object.
Thus, if a finalizable object's finalizer detects that the client didn't call the cleanup method before releasing all references to the object, the finalizer has detected an unmet final condition. In effect, it has detected a programming error, because the client didn't use the object correctly, as defined by the semantics of the object's class.
Bruce and I also realized that a final condition is similar to the pre-conditions, post-conditions, and invariants of Bertrand Meyer's "Design by Contract" approach to programming (Resources). Just as unmet preconditions, postconditions, and invariants can usually be reported, we discussed that perhaps a finalizer could actually report an unmet final condition in some way, such as with an assert or a message in an error log. Bruce felt that the finalizer shouldn't fix the problem by calling the cleanup method, but should just report the problem. I continued to believe in the importance of my Finalizable Object idiom, which states that a finalizer should perform any cleanup the program neglected to perform. But nevertheless, in my discussion of the idiom I raise the possibility of reporting unmet final conditions, in addition to performing the cleanup.