Pattern: Finalization
Context
Smalltalk is a garbage collected system, and Smalltalk objects
do are not normally notified before they are destroyed. In
general such messages are not required, because the memory
occuppied by garbage collected objects is recursively recovered.
On occasion, however, there is a need to perform additional
clean-up operations when an object is no longer needed,
particularly if the object owns some external resource.
Explicitly performing such clean-up operations may be very tricky
to time correctly, because the lifetime of the objects is not
always predictable.
Solution
Mark specific object instances as requiring finalization. Such
objects receive a death-bed notification from the memory manager,
and can perform whatever tidying is necessary.
- Mark an object as requiring finalization by sending it
the #beFinalizable message at an appropriate
point. This is either done in the instance #initialize
method, or preferably after the object has acquired its
external resource(s) (see example).
- Implement a #finalize method to perform the
required finalization. There is no need to remove the
finalization mark as the VM will already have done so.
- If the object has a method for explicitly freeing its
external resource(s) programmatically (which will be the
case if you've followed the Object Liberation
Strategy), then remove the finalization mark
immediately after freeing the resource, by sending the #beUnfinalizable
message. This reduces finalization overhead and allows
objects to die sooner.
- You can remove an object's finalization mark at any other
time that you wish, and that object will not then receive
a #finalize message.
GraphicsTool is the superclass of most of the classes
in Dolphin which encapsulate Windows™ drawing objects.
GraphicsTool adds the finalize mark after creating an
owned external resource:
realize
"Realize (create) the external resource associated with the receiver,
but only if not already realized. Subclasses must implement #basicRealize"
^self isRealized
ifFalse: [
self
basicRealize;
beFinalizable]
Consequences
- Finalization can only occur after a garbage collection
cycle, and is a low priority operation, so clean-up
operations performed this way are far from immediate, and
may be delayed indefinitely if a CPU bound process is
running.
- The objects referenced from finalizable objects cannot be
garbage collected until the objects have been finalized.
- Finalization has considerable overhead relative to
explicitly releasing resources.
- Using the finalization of objects to maintain other data
structures breaks encapsulation, and may require process
synchronisation.
- Finalization is performed asynchronously at an
unpredictable time after the last strong reference to an
object has been removed. Furthermore finalization is
performed after all weak references to an object have
been removed by the garbage collector. This means that
very little can be assumed about the state of the
environment in the #finalize method, and
successful rescue is difficult.
- Order of finalization may be significant, and may be
difficult to guarantee where one finalizable object
weakly references another since there is no notion of
ownership.
Known Uses
- GraphicsTool. Subclasses are wrappers for UI
objects, finalization ensures the external resource is
released..
- File. Finalization is used to ensure the file is
closed.
- FileStream. Finalization is used to ensure that
the streams buffers are flushed to the underlying medium.
- Process. Finalization is used to ensure the
process is correctly terminated so that any unwind blocks
are run.
- ExternalMemory. Finalization is used to ensure
that memory allocated from external heaps is returned.
- OLE COM. Finalization is used to manage the lifetime of
interface objects (IUnknown and subclass) so that
explicit management of the reference count is not
necessary.
Related Patterns
- Finalization is most appropriate for cases where you want
to share objects owning external resources, and will
typically used in conjunction with a Weak Collection.