119
• No overhead is incurred unless the heap becomes exhausted.
• It is impossible for applications to cause memory leaks.
• The application need not be careful with circular references.
Although the process of garbage collection is expensive on the order of a fraction of a second when it occurs, Microsoft claims that the total overhead of garbage collection is on average much less than
the total overhead of reference counting as shown by their benchmarks. This, of course, is highly dependent on the exact pattern of object allocation and deallocation that occurs in any given program.
3.9.1 Finalize
Many objects require some sort of cleanup i.e., finalization when they are destroyed. An example might be a business object that maintains a connection to a database. When the object is no longer in
use, its database connection should be released. The .NET Framework provides a way for objects to be notified when they are about to be released, thus permitting them to release nonmemory resources.
Memory resources held by the object can be ignored because they will be handled automatically by the garbage collector. Heres how it works: the Object class defined in the System namespace has a
method called Finalize that can be overridden. Its default implementation does nothing. If it is overridden in a derived class, however, the garbage collector automatically calls it on an instance of
that class when that instance is about to be reclaimed. Heres an example of overriding the Finalize method:
Public Class SomeClass Protected Overrides Sub Finalize
Release nonmanaged resources here. MyBase.Finalize Important
End Sub End Class
The Finalize method should release any nonmanaged resources that the object has allocated. Nonmanaged resources are any resources other than memory for example, database connections,
file handles, or other OS handles. In contrast, managed resources are object references. As already mentioned, it is not necessary to release managed resources in a Finalize method—the garbage
collector will handle it. After releasing resources allocated by the class, the Finalize method must always call the base classs Finalize implementation so that it can release any resources allocated by
base-class code. If the class is derived directly from the Object class, technically this could be omitted because the Object classs Finalize method doesnt do anything. However, calling it doesnt hurt
anything, and its a good habit to get into.
An objects Finalize method should not be called by application code. The Finalize method has special meaning to the CLR and is intended to be called only by the garbage collector. If youre familiar with
destructors in C++, youll recognize that the Finalize method is the identical concept. The only difference between the Finalize method and C++ destructors is that C++ destructors automatically call
their base class destructors, whereas in Visual Basic .NET, the programmer must remember to put in the call to the base classs Finalize method. It is interesting to note that C—another language on
the .NET platform—actually has destructors as C++ does, but they are automatically compiled into Finalize methods that work as described here.
3.9.2 Dispose
The downside of garbage collection and the Finalize method is the loss of deterministic finalization . With reference counting, finalization occurs as soon as the last reference to an object is released this
is deterministic because object finalization is controlled by program flow. In contrast, an object in a garbage-collected system is not destroyed until garbage collection occurs or until the application exits.
This is nondeterministic because the program has no control over when it happens. This is a problem because an object that holds scarce resources such as a database connection should free those
resources as soon as the object is no longer needed. If this is not done, the program may run out of such resources long before it runs out of memory.
120
Unfortunately, no one has discovered an elegant solution to this problem. Microsoft does have a recommendation, however. Objects that hold nonmanaged resources should implement the
IDisposable interface defined in the System namespace. The
IDisposable interface exposes a
single method, called Dispose, which takes no parameters and returns no result. Calling it tells the object that it is no longer needed. The object should respond by releasing all the resources it holds,
both managed and nonmanaged, and should call the Dispose method on any subordinate objects that also expose the
IDisposable interface. In this way, scarce resources are released as soon as they
are no longer needed. This solution requires that the user of an object keep track of when it is done with the object. This is
often trivial, but if there are multiple users of an object, it may be difficult to know which user should call Dispose. At the time of this writing, it is simply up to the programmer to work this out. In a sense,
the Dispose method is an alternate destructor to address the issue of nondeterministic finalization when nonmanaged resources are involved. However, the CLR itself never calls the Dispose method. It
is up to the client of the object to call the Dispose method at the appropriate time, based on the clients knowledge of when it is done using the object. This implies responsibilities for both the class author
and client author. The class author must document the presence of the Dispose method so that the client author knows that its necessary to call it. The client author must make an effort to determine
whether any given class has a Dispose method and, if so, to call it at the appropriate time.
Even when a class exposes the IDisposable
interface, it should still override the Finalize method, just in case the client neglects to call the Dispose method. This ensures that nonmanaged resources
are eventually released, even if the client forgets to do it. A simple but incomplete technique would be to place a call to the objects Dispose method in its Finalize method, like this:
Incomplete solution. Dont do this. Public Sub Dispose Implements IDisposable.Dispose
Release resources here. End Sub
Protected Overrides Sub Finalize Dispose
MyBase.Finalize End Sub
In this way, if the client of the object neglects to call the Dispose method, the object itself will do so when the garbage collector destroys it. Microsoft recommends that the Dispose method be written so it
is not an error to call it more than once. This way, even if the client calls it at the correct time, its OK for it to be called again in the Finalize method.
If the object holds references to other objects that implement the IDisposable
interface, the code just shown may cause a problem. This is because the order of object destruction is not guaranteed.
Specifically, if the Finalize method is executing, it means that garbage collection is occurring. If the object holds references to other objects, the garbage collector may have already reclaimed those
other objects. If the object attempts to call the Dispose method on a reclaimed object, an error will occur. This situation exists only during the call to Finalize—if the client calls the Dispose method,
subordinate objects will still be there. They cant have been reclaimed by the garbage collector because they are reachable from the applications code.
To resolve this race condition, it is necessary to take slightly different action when finalizing than when disposing. Here is the modified code:
Public Sub Dispose Implements IDisposable.Dispose DisposeManagedResources
DisposeUnmanagedResources End Sub
Protected Overrides Sub Finalize DisposeUnmanagedResources
121
MyBase.Finalize End Sub
Private Sub DisposeManagedResources Call subordinate objects Dispose methods.
End Sub Private Sub DisposeUnmanagedResources
Release unmanaged resources, such as database connections. End Sub
Here, the Finalize method only releases unmanaged resources. It doesnt worry about calling the Dispose method on any subordinate objects, assuming that if the subordinate objects are also
unreachable, they will be reclaimed by the garbage collector and their finalizers and hence their Dispose methods will run.
An optimization can be made to the Dispose method. When the Dispose method is called by the client, there is no longer any reason for the Finalize method to be called when the object is destroyed.
Keeping track of and calling objects Finalize methods imposes overhead on the garbage collector. To remove this overhead for an object with its Dispose method called, the Dispose method should call the
SuppressFinalize shared method of the GC class, like this:
Public Sub Dispose Implements IDisposable.Dispose DisposeManagedResources
DisposeUnmanagedResources GC.SuppressFinalizeMe
End Sub
The type designer must decide what will occur if the client attempts to use an object after calling its Dispose method. If possible, the object should automatically reacquire its resources. If this is not
possible, the object should throw an exception. Ex am ple 3- 1
shows the latter.
Example 3-1. A complete FinalizeDispose example
Public Class SomeClass Implements IDisposable
This member keeps track of whether the object has been disposed. Private disposed As Boolean = False
The Dispose method releases the resources held by the object. It must be called by the client when the client no longer needs
the object. Public Sub Dispose Implements IDisposable.Dispose
If Not disposed Then DisposeManagedResources
DisposeUnmanagedResources GC.SuppressFinalizeMe
disposed = True End If
End Sub The Finalize method releases nonmanaged resources in the case
that the client neglected to call Dispose. Because of the SuppressFinalize call in the Dispose method, the Finalize method
will be called only if the Dispose method is not called. Protected Overrides Sub Finalize
DisposeUnmanagedResources MyBase.Finalize
End Sub
122
Private Sub DisposeManagedResources Call subordinate objects Dispose methods.
End Sub Private Sub DisposeUnmanagedResources
Release unmanaged resources, such as database connections. End Sub
Private Sub DoSomething Call the EnsureNotDisposed method at the top of every method that
needs to access the resources held by the object. EnsureNotDisposed
... End Sub
Private Sub EnsureNotDisposed Make sure that the object hasnt been disposed.
Instead of throwing an exception, this method could be written to reacquire the resources that are needed by the object.
If disposed Then Throw New ObjectDisposedExceptionMe.GetType .Name
End If End Sub
End Class
3.10 A Brief Tour of the .NET Framework Namespaces