About This Site
Concepts for Persistent Objects
Language Design Principles
An Owning Pointer
What Is Lambda?
How I Learned to Write Comments
Finalization in JavaI've recently been learning Java, after having spent years programming mostly in C++. In general, Java seems to me to be righteous and well-designed, but there is one aspect of it I don't like, and that's the finalization system, with which I have two complaints.
First, most of the code that is supposed to go in the finalize method—clearing references to other objects and invoking the finalize method of the superclass—is busywork that could have been avoided. It was from the page Writing a finalize Method in Sun's Java Tutorial that I learned these things were desirable, and even there there was no ringing endorsement.
To be complete and well-behaved, the Stack class should release its reference to the Vector.
As far as I can tell from the language specification, the only thing clearing the references does is allow memory to be freed a little sooner than otherwise. Suppose, for example, that there are three finalizable objects A, B, and C, with no references to any of them except that object A refers to B, and B to C. Suppose also that the objects are finalized in reverse order. After all three have been finalized, all are unreachable and can be freed, regardless of whether or not the references were cleared. The only time when clearing the references makes a difference is after B has been finalized. At that time, A has not been finalized, and still holds a reference to B, so both are f-reachable. If, however, the reference from B to C has been cleared, then C will already be unreachable.
Another way of looking at my complaint is to say that having to write this cleanup code seems like a step backward from C++. In C++, once the basic classes that represent ownership—such as owning pointers and owning lists—are implemented, most destructors are empty.
As a slight attempt at being constructive, it seems to me that if Java can implement functions such as cloning and serialization at the Object level, it could also implement finalization at the same level. The standard pattern would be to require each class to enable finalization by implementing the empty interface Finalizable, but I doubt that would be necessary or useful—if allowing classes to override the finalize method isn't sufficient, allowing them to implement an empty interface NotFinalizable should be.
My second complaint also arises from comparison to C++. In C++, destructors of helper classes could be used to implement all kinds of nice exception-handling behavior. In Java, however, because finalization is not guaranteed to occur promptly or in any particular order, it is always necessary to use a try-catch or try-finally block.
To be concrete, here is an example of nice exception-handling behavior. There are two reasons you wouldn't want to write this code out as a try-finally block: first, it is general and reusable; second, it is nontrivial and contains state, and hence would be bug-prone if rewritten over and over.
public class Transaction
I've written this in Java because the Java SQL classes are simple and standardized, but it won't work right because finalization isn't performed promptly. It can be made to work with code like the following,
but this is prone to the bug of forgetting the try-finally block, and for cleanness you'd want to add a second boolean to prevent multiple closes. (You'd also want to rename “finalize” to “close”, as I've done, so that when the try-finally block is forgotten, you don't get transaction rollbacks occurring at unpredictable times.)
I have a somewhat constructive idea here, too: add reference counts to Java objects, along with a rule that when a reference count goes to zero, the object is finalized immediately—ideally, in the same thread. This would not replace garbage collection, which would still be needed to remove loops. Since the existing Java language specification leaves the order and timing of finalization indefinite, this change would be backward-compatible with existing code.
One problem with this idea is that the finalize method can throw exceptions. However, I don't see this as a problem, but as another peculiarity of Java. My experience with C++ has led me to think that destructors should be coded so as not to throw exceptions, and it is surprising to me that Java allows it.
@ April (2000)