Dispose Memory Management and Garbage Collection

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