Skip to main content

Finally, a Good Use for Finalizers

Posted by tball on August 17, 2005 at 3:46 AM PDT

For years Java developers have been warned about the dangers of using finalize methods to release system resources. Josh Block describes the issues thoroughly in his book, Effective Java (Item 6: Avoid finalizers), but just Google for "avoid finalizers" or "finalizers considered harmful" to find hundreds of similar discussions not just about Java, but most other languages that offer the facility. Yet I just found out that the problem cropped up again in a recent internal Mustang build. What is it that is so alluring about this bad programming practice that it can blind even very experienced Java developers with its false charms?

Finalizers bit me hard back in the JDK pre-1.0 days, because they were originally used to free the Windows resources from AWT graphic objects. This technique gave Tumbling Duke the power to cause the Blue Screen of Death, but such power is cannot be entrusted to a mere applet, or for any class for that matter. Any class that allocates a critical resource needs an explicit "close", "release" or similarly named method in its API. Its clients must then always use that method.

The problem is that a library provider cannot always regulate its use, or misuse, by developers who use that library. The java.io documentation, for example, can warn, argue, even plead that developers close file streams after using them, but it cannot enforce that requirement. So what a lot of library engineers do is add a fallback routine in a finalize method to check whether a resource has been released, and if not, release it during finalization. It's an ugly hack, but often necessary to avoid granting Tumbling Duke-like powers to their client programs.

But cleaning up after one's messy clients doesn't encourage good behavior on their part. I found this to be true while on vacation this summer with our children, who didn't understand why food wrappers cannot just be thrown on our hotel floor since a maid will pick them up later. "Because you have to" may work for making children clean up after themselves (it didn't for me, but maybe for other parents), but such an approach is guaranteed not to work for developers. As long as the maid (or library) silently cleans up, messes will be left to clean.

But what if the maid left a nasty note whenever such a mess was left? Something like (using java.util.Logger):

protected void finalize() throws Throwable {
   if (handle != null) {
       Logger.global.log(Level.WARNING, "my handle not released"); // new
       release();
   }
}

Now when a client tests his poorly written code which seemed to work because the library was silently cleaning up after it, several nasty-grams start appearing. Even better, add a stack trace which will point to where in the client code is the problem:
private Throwable trace;
...
   // during handle allocation
   callerTrace = new Throwable("handle allocation");
...
protected void finalize() throws Throwable {
   if (handle != null) {
       Logger.global.log(Level.WARNING, "my handle not released", trace); // new      
       release();
   }
}

This takes advantage of how scary stack traces are to clients, plus it forwards all the information a library engineer has to the offending developer. Finally, we have a way finalizers can improve quality instead of detracting from it, by throwing up warning flags when library resources are being leaked.

Related Topics >>