prev back next

Object finalization

Introduction

Normally, the runtime system of Smalltalk/X (and all other Smalltalk implementations, of course) performs garbage collection on its own. There is no need for the programmer to free objects; typically, the program does not even get any notification when objects are freed.
Objects which are no longer reachable by any other reachable object are automatically returned to the free memory pool (see garbage collection).
However, for some classes, cleanup actions are needed whenever an instance dies.
Examples are: In general, all objects which somehow represent or refer to external resources (i.e. operatingSystem or shared-object) need some cleanup action to release that external resource. This can be done either manually by the programmer or automatically by the runtime system.

For the programmer, it is much easier (and less error prone) to use the finalization mechanism, instead of doing the housekeeping herself. Just think of a bitmap image with a local colormap; how would you know who else in the system uses those colors and when they can be freed without danger ?

To support object finalization, Smalltalk/X includes the WeakArray class, which provides a low level mechanism and Registry which offers a more programmer friendly higher level interface.

WeakArray: the low level mechanism

Instances of WeakArray are arrays which do not prevent their elements from being garbage collected. This means that the reference from the weakArray to an object does not count as a reference with respect to garbage collection.
In other words: the garbage collector will consider an object as being free if either no references or only references from weakArrays to that object exist.
Whenever such an object is found, the object is returned to the free memory pool as usual. and the entry in the weakArray is nilled. Finally, the garbage collector will inform all dependents of the weakArray about the change.

Using this basic mechanism for object finalization is straightforward:

keep the relevant information (such as fileDescriptor, window handle etc.) in another array,
and references to the corresponding smalltalk object in a weakArray.
Whenever the weakArray gets informed about some object being reclaimed, scan it for nil entries and free the corresponding resource as stored in the other array (i.e. close the file, free the window handle etc.).

Another use of weakArrays is with caching, for example, you can keep some recently used data in a weakArray, reusing it if present. The cache will be automatically 'flushed' by the garbage collector in regular intervals.

Registry: the programmer-friendly mechanism

Since weakArrays are a bit complicated to use, another class called Registry is provided. Registries internally perform the above operations to keep track of reclaimed objects. However, instead of storing some device handles in the other array, they keep shallow copies of the original objects there, and send a notification to this copy.
The programmer can code as if the original received the destroy notification and program as follows:
The object which wants to do some finalization actions should be registered in a registry, and will later (at finalization time) receive a disposed-message.
In thise method, perform cleanup as required. For example, close an underlying file by doing "self closeFile".
Technically, we cannot send messages to the original object, since that object is already gone at that time. Instead, the shallow copy created by the registry will receive the disposed message. Since the copy has the same contents as the original (especially: fileDescriptors and window handles), its disposed method can be written as if it was the original.

Lets summarize what is needed for proper finalization:

You should keep in mind, that the receiver of the dispose message is a shallow copy, not the original. Although in most cases, this is transparent, there are situations where it does make a difference: when keeping objects in identitySets or identityDictionaries, which compare for identity instead of equality. The shallow copy will of course never be identical, but can be made equal to the original.

Since creating a shallow copy may be expensive (especially for big or heavily used objects), the copy is created using a slighly different copy method: shallowCopyForFinalization.
The default implementation for this (in Object) simply recalls shallowCopy.
You may want to redefine this method in your class - especially if you do not need all instance variables at finalization time.
For example, the Cursor classes disposed method will only need the cursors resource-id - therefore, a special shallowCopyForFinalization has been defined, which creates an empty copy with only the id being copied.

See example uses in Form, Cursor, Color, Font or ExternalStream for how registries are used, and the implementation of Registry for more protocol information.


Copyright © Claus Gittinger Development & Consulting, all rights reserved

(cg@ssw.de)