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
WeakArray class, which provides a low level
Registry which offers a more
programmer friendly higher level interface.
WeakArrayare 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 set to 0 (zero). 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,(Notice, that a HandleRegistry is better suited for this concrete application.)
and references to the corresponding smalltalk object in a WeakArray.
Whenever the WeakArray informs its dependents about some object being reclaimed, scan it (in your
#update:with:frommethod) for zero 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. (a concrete example is found in the
Method » source
method, which keeps the recently used sourcefile open for a while.)
The object which wants to do some finalization actions should be registered in a registry, and will later (at finalization time) receive aTechnically, we cannot send messages to the original object, since that object is already gone at that time. Instead, the executor created by the registry will receive the
In this method, perform cleanup as required. For example, close an underlying file by doing
#finalizemessage. Since the executor has the same contents as the original (especially: fileDescriptors and window handles), its
#finalizemethod can be written as if it was the original.
Lets summarize what is needed for proper finalization:
#finalizemethod for cleanup.
#finalizemessage is a shallow copy, not the original (that has already been garbage collected!). 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 executor 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
The default implementation for this (in
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
will only need the cursors resource-id - therefore, a special
#executor has been defined, which creates a
lightweight copy with only the id being copied.
Classes with many instance variables may avoid to create big finalization objects by returning a small placeHolder object (handle-holder), which does not use up as much memory as the original
HandleRegistries are very useful if some other object wants to keep
track of living objects and perform cleanup actions, which the
other objects cannot do itself.
A typical example is a communication system, which handles an IPC connection for multiple lightweight processes, and wants to be informed if any of them dies, to close the corresponding connection.
In this setup, a HandleRegistry would assign an IPC-channel ID for each process and inform the watcher if any process dies. (an alternaitive is to define a subclass of process, which does the cleanup in its finalization method - but that seems to be an ugly kludge). To use a handleRegistry, the following steps are required:
#update:with:from:method for cleanup.
See example uses in
ExternalStream for how
registries are used,
and the implementation of
Registry for more protocol information.
In addition, you will find more examples in the file:
Copyright © 1995 Claus Gittinger Development & Consulting