FileStreams
, Sockets
etc.
Color
, Font
,
View
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
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 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:from
method) 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.)
Registry
and
HandleRegistry
are provided.
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#finalize
-message.
In this method, perform cleanup as required. For example, close an underlying file by doing"self closeFile"
.
#finalize
message.
Since the executor has the same contents as the original
(especially: fileDescriptors
and window handles), its #finalize
method can be written as if it
was the original.
Lets summarize what is needed for proper finalization:
#initialize
method)
#finalize
method for cleanup.
#finalize
message 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
copy method: #executor
.
The default implementation for this (in Object
)
simply sends #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 #finalize
method
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
HandleRegistry
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:
#initialize
method)
#register:as:
message.
#update:with:from:
method for cleanup.
See example uses in Form
, Cursor
, Color
,
Font
or 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: "doc/coding/Registry-examples.st"
.
Copyright © 1995 Claus Gittinger, all rights reserved
<cg at exept.de>