Chapter 5. Garbage Collection Gotchas

Garbage collection, in general, is the process of cleaning up the memory and resources used by an object when it is no longer needed. For instance, in C++, you write a destructor to take care of this. There are two tasks that the destructor typically performs:

  • deletes other objects to which the object holds pointers

  • closes handles to resources

.NET, on the contrary, provides automatic garbage collection. It largely removes the burden of memory management from the programmer. But what does automatic garbage collection really do? It just reclaims the memory used by objects. What about the cleanup of resources?

When I say resources here, I mean unmanaged resources: those that .NET will not be able to release automatically. For example, resources created while interacting with a COM component or while using PInvoke to access some Win32 API. You, the programmer, have to write code to properly dispose of these resources.

The Finalize() method is provided for this purpose. When an object is garbage-collected, the CLR takes care of memory-related cleanup and calls the object’s Finalize() method, giving it an opportunity to release any unmanaged resources.

In VB.NET, you have to actually write the Finalize() method. In C#, you don’t. Instead, you write a specialized pseudo-destructor with the ~ NameOfYourClass () syntax, as in C++. But don’t confuse the C# pseudo-destructor with the C++ destructor. The generated MSIL does not contain any destructor. The C# compiler creates the Finalize() method using the code you write in the specialized pseudo-destructor.

Tip

Throughout the rest of this chapter, whenever I refer to the "Finalize() method,” I am referring to the Finalize() method in VB.NET and the pseudo-destructor in C#.

The CLR is responsible for reclaiming memory; your object’s Finalize() method releases unmanaged resources. This appears reasonable and straightforward, but a number of practical issues surface during development. This chapter focuses on concerns related to garbage collection, and how to write code that handles it effectively.

GOTCHA #35 Writing Finalize() is rarely a good idea

An object is said to be accessible if traversing from a reference on the stack will lead you to it, directly or indirectly. When an object becomes inaccessible, it is ready for garbage collection, since no part of your code needs to access its content. When it is actually removed is up to the garbage collector. If this object implements a Finalize() method, it is executed at the time of its cleanup by the CLR.

The state change an object undergoes during the cleanup phase is shown in Figure 5-1. It first goes from accessible (A) to inaccessible (I). While in this “zombie” state, it occupies memory, but you cannot execute any of its methods or access any of its fields or properties. (But sometimes it may look like you can. See Gotcha #36, "Releasing managed resources in Finalize() can wreak havoc." The object may remain in this inaccessible state anywhere from fractions of seconds to hours (or longer), depending on the garbage collector and the memory usage of the application. When the garbage collector eventually decides to clean the object up, it inspects it to see if the Finalize() method needs to be executed. If so, the object is brought to the resurrected state (R) and becomes accessible once again. Once the Finalize() method has run, the object goes to the Inaccessible-no-need-to-finalize state (IF). Then the memory occupied by the object is reclaimed and the object becomes non-existent (N).

Jeffrey Richter explains the CLR garbage-collection mechanism in great detail. Refer to "Garbage Collection" in the section "on the web" in the Appendix.

If the object does not have a Finalize() method, the cleanup will be faster. It can go from the inaccessible state (I) to the non-existent state (N) directly, as shown in Figure 5-1 by the dashed line labeled with a question mark.

Should you implement the Finalize() method? Consider Example 5-1.

States of an object during its cleanup phase

Figure 5-1. States of an object during its cleanup phase

Example 5-1. Code with Finalize()

C# (Finalize)

using System;

namespace WhyNoFinalize
{
    public class SomeClass
    {
        private SomeOtherClass ref1;

        public SomeClass(SomeOtherClass givenObject)
        {
            ref1 = givenObject;
        }

        ~SomeClass()
        {
            ref1 = null;
        }
    }
}

VB.NET (Finalize)

Public Class SomeClass
    Private ref1 As SomeOtherClass


    Public Sub New(ByVal givenObject As SomeOtherClass)
        ref1 = givenObject
    End Sub
    Protected Overrides Sub Finalize()

               ref1 = Nothing
        MyBase.Finalize()
    End Sub
End Class

In this example, SomeClass holds a reference to an instance of SomeOtherClass. The Finalize() method of SomeClass sets the reference ref1 to null/Nothing. Is this necessary?

No, it isn’t. Whenever an instance becomes inaccessible, so do all of the other objects that are accessible only through that instance. So, when an object of SomeClass becomes unreachable, the object of SomeOtherClass that ref1 refers to does also. Once a group of objects becomes inaccessible, all of them might be collected during the next garbage-collection sweep. There is no guarantee in which order their Finalize() methods will be called. So the object of SomeOtherClass that ref1 refers to may have already been removed by the time SomeClass.Finalize() is called! Setting ref1 to null/Nothing is redundant. But writing the Finalize() method provides code for the garbage collector to execute, and obliges it to resurrect the object, thus unnecessarily slowing it down. Even worse, the memory is not reclaimed until the next garbage-collection sweep, further delaying the cleanup.

IN A NUTSHELL

Do not write the Finalize() method unless you have a good reason. It makes more work for the garbage collector. The main reason for implementing Finalize() is to make sure you free unmanaged resources, as you’ll see in Gotcha #36, "Releasing managed resources in Finalize() can wreak havoc." Only write it in the context of the Dispose design pattern (see Gotcha #40, "Implementing IDisposable isn’t enough“).

GOTCHA #36 Releasing managed resources in Finalize() can wreak havoc

Do not access managed resources within the Finalize() method. Typically objects may have dependencies on other objects. In a language like C++, it is not unusual within a destructor to communicate with associated objects. However, when working with .NET, you should not carry those practices over to the Finalize() method.

You may ask, “So what if I access other objects within Finalize()?” The reason you shouldn’t is that the result cannot be predicted. You have no idea of the order in which the garbage collector will call the Finalize() method of your objects. You may write code that accesses other objects within Finalize(), run your program, and say, “See, it works!” The phrase “it works” is a very unpleasant one in programming. It’s like saying, “See, I drove the wrong direction on a one-way street and nothing went wrong.” In such cases, it is not a question of if things will go wrong, but when. This is illustrated in Example 5-2.

Example 5-2. Accessing managed resources

C# (NotToFinalize)

//SomeOtherClass.cs
using System;

namespace FinalizeAndManagedResources
{
    public class SomeOtherClass
    {
        private int[] values = new int[1000000];

        public void Notify()
        {
            Console.WriteLine("Notify called on SomeOtherClass");
        }

        ~SomeOtherClass()
        {
            Console.WriteLine("*** SomeOtherClass Finalized");
        }
    }
}

//SomeClass.cs
using System;

namespace FinalizeAndManagedResources
{
    public class SomeClass
    {
        private SomeOtherClass other;

        public void Setother(SomeOtherClass otherObject)
        {
            other = otherObject;
        }

        ~SomeClass()
        {
            Console.WriteLine("Finalize called on SomeClass");
            if(other != null)
            {
        other.Notify();
            }
        }
    }
}

//Test.cs
using System;

namespace FinalizeAndManagedResources
{
    class Test
    {
        [STAThread]
        static void Main(string[] args)
        {
    SomeClass object1 = new SomeClass();
    SomeOtherClass object2 = new SomeOtherClass();
    object1.Setother(object2);
        }
    }
}

VB.NET (NotToFinalize)

'SomeOtherClass.vb
Public Class SomeOtherClass
    Private values() As Integer = New Integer(1000000 - 1) {}

    Public Sub Notify()
        Console.WriteLine("Notify called on SomeOtherClass")
    End Sub


    Protected Overrides Sub Finalize()

        MyBase.Finalize()
        Console.WriteLine("*** SomeOtherClass Finalized")
    End Sub
End Class


'SomeClass.vb
Public Class SomeClass
    Private other As SomeOtherClass

    Public Sub Setother(ByVal otherObject As SomeOtherClass)
        other = otherObject
    End Sub
    Protected Overrides Sub Finalize()

        MyBase.Finalize()
        Console.WriteLine("Finalize called on SomeClass")
        If Not other Is Nothing Then
    other.Notify()
        End If
    End Sub
End Class


'Test.vb
Module Test

    Sub Main()
        Dim object1 As SomeClass = New SomeClass
        Dim object2 As SomeOtherClass = New SomeOtherClass
        object1.Setother(object2)
    End Sub

End Module

In this example, an instance of SomeClass has a reference to an instance of SomeOtherClass. When the object of SomeClass is finalized, it calls the Notify() method on the SomeOtherClass object. In the Main() method of the Test module, you create instances of the two classes and relate them using the SetOther() method of SomeClass. When the program completes execution, both the objects are garbage collected. The question is, in which order?

Figure 5-2 shows one possible scenario. (One of my reviewers asked how many times I had to run the program to get this output. It took only one run!)

The Notify() method is called on the object after its Finalize() method has already executed. This is strange behavior and may lead to unpredictable results. Had it thrown an exception or crashed, it would be easy to realize the problem and fix it. Because it looks like it works, the problem remains obscured and unfixed.

Output from Example 5-2

Figure 5-2. Output from Example 5-2

Here’s another issue to consider. Suppose the method called during finalization is trying to be a good thread-safe citizen. Take a look at Example 5-3. Though this example is somewhat contrived, it starkly illustrates the risks of accessing managed resources in Finalize().

Example 5-3. Inadvertently locking from within Finalize()

C# (NotToFinalize)

//SomeOtherClass.cs
using System;

namespace FinalizeAndManagedResources
{
    public class SomeOtherClass
    {
        private int[] values = new int[1000000];

        public void Notify()
        {
            Console.WriteLine("Entering Notify");
    lock(this)
            {
                Console.WriteLine("Notify called on SomeOtherClass");
            }
        }

        ~SomeOtherClass()
        {
            Console.WriteLine("*** SomeOtherClass Finalized");
        }
    }
}


//SomeClass.cs
using System;

namespace FinalizeAndManagedResources
{
    public class SomeClass
    {
        private SomeOtherClass other;

        public void Setother(SomeOtherClass otherObject)
        {
            other = otherObject;
        }

        ~SomeClass()
        {
            Console.WriteLine("Finalize called on SomeClass");
            if(other != null)
            {
        other.Notify();
            }
        }
    }
}


//Test.cs
using System;

namespace FinalizeAndManagedResources
{
    class Test
    {
        private static void ohoh(SomeOtherClass obj)
        {
            SomeClass object1 = new SomeClass();
            object1.Setother(obj);
        }

        [STAThread]
        static void Main(string[] args)
        {
            SomeOtherClass object2 = new SomeOtherClass();

            lock(object2)
            {
                ohoh(object2);
                GC.Collect();
                //GC.WaitForPendingFinalizers();
                Console.WriteLine("OK let's release lock in Main");
            }

            Console.WriteLine("Are we here");
        }
    }
}

VB.NET (NotToFinalize)

'SomeOtherClass.vb
Public Class SomeOtherClass
    Private values() As Integer = New Integer(1000000 - 1) {}

    Public Sub Notify()
        Console.WriteLine("Entering Notify")
        SyncLock Me
            Console.WriteLine("Notify called on SomeOtherClass")
        End SyncLock
    End Sub


    Protected Overrides Sub Finalize()
        MyBase.Finalize()
        Console.WriteLine("*** SomeOtherClass Finalized")
    End Sub
End Class


'SomeClass.vb
Public Class SomeClass
    Private other As SomeOtherClass

    Public Sub Setother(ByVal otherObject As SomeOtherClass)
        other = otherObject
    End Sub
    Protected Overrides Sub Finalize()
        MyBase.Finalize()
        Console.WriteLine("Finalize called on SomeClass")
        If Not other Is Nothing Then
    other.Notify()
        End If
    End Sub
End Class


'Test.vb
Module Test
    Sub ohoh(ByVal obj As SomeOtherClass)
        Dim object1 As New SomeClass
        object1.Setother(obj)
    End Sub

    Sub Main()
        Dim object2 As New SomeOtherClass

        SyncLock object2
            ohoh(object2)
            GC.Collect()
            'GC.WaitForPendingFinalizers()
            Console.WriteLine("OK let's release lock in Main")
        End SyncLock
        Console.WriteLine("Are we here")
    End Sub

End Module

Within the Main() method you get a lock on an object of SomeOtherClass. Then you create an instance of SomeClass and the instance of SomeOtherClass is associated with it. When GC.Collect() is invoked, since the instance of SomeClass is no longer accessible, its Finalize() is called. Within this Finalize() method, you invoke Notify() on the instance of SomeOtherClass. In Notify(), you lock the object. However, the main thread within the Main() method already owns a lock on the object. This blocks the garbage collector thread—it cannot continue until the Main() method completes. (Main() is able to run to completion because the call to GC.Collect() does not block, even though the CLR’s finalization thread does.) You can see this in the output from the program, shown in Figure 5-3.

Output from Example 5-3

Figure 5-3. Output from Example 5-3

Let’s take this a bit further. The GC class’s WaitForPendingFinalizers() method suspends the calling thread until the finalization thread has run the Finalize() method on all eligible objects in its queue. Now what happens if you uncomment the GC.WaitForPendingFinalizers() in the Main() method in Example 5-3? The output is shown in Figure 5-4.

Deadlock after GC.WaitForPendingFinalizers() was uncommented in Example 5-3

Figure 5-4. Deadlock after GC.WaitForPendingFinalizers() was uncommented in Example 5-3

The program is deadlocked. You may say, well, don’t call GC.WaitForPendingFinalizers(), or GC.Collect() for that matter. But what if you had called some other method on the object from another thread, and that thread holds a lock on the object while it waits for something else? There is a high probability of deadlock occurring if you access managed resources within the Finalize() method.

IN A NUTSHELL

Do not touch any managed objects within the Finalize() method. Do not call any methods on other objects, or even set their references to null/Nothing. This could lead to unpredictable results, including the possibility of crash or deadlock. Only release unmanaged resources within Finalize().

GOTCHA #37 Rules to invoke base.Finalize() aren’t consistent

Object.Finalize() is an odd method. It is defined as protected overridable. As mentioned in the introduction to this chapter, in C# you implement your class’s Finalize() method with the special syntax ~ NameOfClass, where NameOfClass is the actual name of your class. This pseudo-destructor is compiled into the Finalize() method during the MSIL translation.

Furthermore, within the C# destructor, you should not attempt to call Finalize() on the base class. The compiler-generated Finalize() takes care of calling the base class’s Finalize(), even if an exception is raised, because the call is safe inside a finally block.

For example, look at this pseudo-destructor:

        ~MyClass()
        {
            // Whatever cleanup code you write
        }

The C# compiler will translate this into the Finalize() method in your assembly. A look at the generated MSIL using ildasm.exe will clarify this for you (see Figure 5-5).

MSIL translation of a C# pseudo-destructor

Figure 5-5. MSIL translation of a C# pseudo-destructor

If a C# programmer ever has to work with VB.NET code, he should remember that the rules are different. In VB.NET, you write Finalize() as a Protected Overrides method, and you must call MyBase.Finalize(), as shown in Example 5-4.

Example 5-4. Writing Finalize() in VB.NET

               Protected Overrides Sub Finalize()
        Try
            'Whatever cleanup code you write
        Finally
               MyBase.Finalize()
        End Try
    End Sub

However, if you use the Dispose design pattern (refer to Gotcha #40, "Implementing IDisposable isn’t enough“), then you most likely will not implement the Finalize() method in your derived classes.

IN A NUTSHELL

In C#, do not invoke the base class’s Finalize() method from within the destructor. In VB.NET, do invoke the base class’s Finalize() method from within the Finalize() method.

GOTCHA #38 Depending on Finalize() can tie up critical resources

When an object becomes inaccessible it may remain in that state for an unpredictable amount of time. The garbage collector decides when the memory needs to be reclaimed based on its own heuristics, which include factors such as the amount of memory being used. The problem is that the time between when you have quit using the object and when Finalize() is called on it might be very long. If your object is using some critical unmanaged resources, you will be holding onto them in the meantime. This may lead to resource contention that is not detected or handled well within your .NET program.

Consider the case where a .NET object communicates with a COM component running in a different process. The .NET object is very small (say only a few bytes in size). The COM object it communicates with, however, is large. Now, say you have a loop where you create one .NET object per iteration, using it and letting go of the reference. When will the unmanaged resource be released?

The answer, based on what you’ve read so far in this chapter, is eventually-- but who knows exactly when?

Fortunately, .NET provides a mechanism to support synchronous cleanup of resources used by objects. If an object implements the IDisposable interface, it lets its users know that it offers a Dispose() method they can call to release the object. (Dispose() is the only method defined by IDisposable.) Because the users call Dispose() while the object is still accessible, it can free both managed and unmanaged resources.

An illustration of this is shown in Example 5-5. The code defines the Wrapper class, and states that it implements IDisposable. The Dispose() method cleans up the COM object by calling the ReleaseComObject() method of the System.Runtime.InteropServices.Marshal class. As a fail-safe mechanism to make sure the COM object gets released, the Finalize() method does the same thing. (ReleaseComObject() is discussed in Gotcha #65, “Release of COM object is confusing.”)

Example 5-5. Using unmanaged resources from .NET

C# (ResourceHold)

//Wrapper.cs
using System;
using ACOMCompLib;

namespace FinalizePeril
{
    public class Wrapper : IDisposable
    {
        IMyComp comp = new MyCompClass();

        public int doSomething()
        {
            int result;
    comp.doSomething(out result);
            return result;
        }

        ~Wrapper()
        {
    System.Runtime.InteropServices.Marshal.ReleaseComObject(comp);
        }

        #region IDisposable Members
                

               public void Dispose()
        {
    System.Runtime.InteropServices.Marshal.ReleaseComObject(comp);
        }

        #endregion
    }
}

//Test.cs
using System;
using ACOMCompLib;

namespace FinalizePeril
{
    class Test
    {
        [STAThread]
        static void Main(string[] args)
        {
            int iterations = Convert.ToInt32(args[0]);
            int result = 0;
    for(int i = 0; i < iterations; i++)
            {
        Wrapper theWrapper = null;
                try
                {
            theWrapper = new Wrapper();
               result = theWrapper.doSomething();
                }
                finally
                {
            theWrapper.Dispose();
                }
            }

            Console.WriteLine(result);
            Console.WriteLine("End of Main");
        }
    }
}

VB.NET (ResourceHold)

'Wrapper.vb

Imports ACOMCompLib

Public Class Wrapper
    Implements IDisposable

               Dim comp As IMyComp = New MyCompClass


    Public Function doSomething() As Integer
        Dim result As Integer
        comp.doSomething(result)
        Return result
    End Function

    Public Sub Dispose() Implements System.IDisposable.Dispose
        System.Runtime.InteropServices.Marshal.ReleaseComObject(comp)
    End Sub

    Protected Overrides Sub Finalize()
        MyBase.Finalize()
        System.Runtime.InteropServices.Marshal.ReleaseComObject(comp)
    End Sub

End Class

'Test.vb
Module Test

    Sub Main(ByVal args() As String)
        Dim iterations As Integer = Convert.ToInt32(args(0))
        Dim result As Integer = 0

        For i As Integer = 0 To iterations - 1
            Dim theWrapper As Wrapper

            Try
        theWrapper = New Wrapper
        result = theWrapper.doSomething()
            Finally
        theWrapper.Dispose()
            End Try

            Console.WriteLine(result)
            Console.WriteLine("End of Main")
        Next
    End Sub

End Module

In the example, one instance of Wrapper is created for each pass through the loop. Each object of Wrapper has a reference to a COM component. What would happen if you didn’t call theWrapper.Dispose()? Well, if the COM component is in-process, you don’t have much to worry about. As the memory usage increases, the garbage collector will kick in and Finalize() the inaccessible objects. However, if the COM component is out-of-process, then you are simply out of luck, especially if the COM components are large. As far as the CLR is concerned it will have no reason to garbage-collect because the memory utilization on its side is pretty low. As a result, it will not call Finalize().

A good analogy is to consider having two small tables of the same size. On one table you place very small objects, say peanuts. For each peanut you place on the first table, you place a larger object on the second table, say a watermelon. The second table will fill up pretty quickly, while the first table has a lot of free space. This is exactly what happens in the example above if you neglect to call theWrapper.Dispose(). The Wrapper and the Runtime Callable Wrapper (RCW) that it uses are very small objects and thousands of these can be held in memory. However, if the related COM objects are watermelon-sized, you get into memory contention on the COM component server side. By calling theWrapper.Dispose(), you make sure the COM objects get released each time through the loop.

You can’t predict exactly when Finalize() will be called on an object; thus, any resource not released by the programmer will be held until that happens. This could cause some undesirable side effects. By implementing the IDisposable interface and its Dispose() method, you give users of your object a way to clean it up synchronously.

As I pointed out earlier, the Finalize() and Dispose() methods of the Wrapper class both call ReleaseComObject(). Of course, you don’t want to repeat the code in both. (Refer to the Don’t Repeat Yourself (DRY) principle in [Hunt00].) You’ll see how to refactor these in Gotcha #40, "Implementing IDisposable isn’t enough."

IN A NUTSHELL

Do not rely on the CLR to call the Finalize() method. Allow users of your object to properly clean up by implementing the System.IDisposable interface. This way, they can call the Dispose() method on the object when they are done using it.

GOTCHA #39 Using Finalize() on disposed objects is costly

It is better to Dispose() an object than to depend on its Finalize() being called, because Dispose() lets you control the cleanup of an object (see Gotcha #38, "Depending on Finalize() can tie up critical resources“). Also, there is overhead if the garbage collector needs to call Finalize() (see Gotcha #39, "Using Finalize() on disposed objects is costly“). If an object has been properly disposed of (by the call to Dispose()), there is no need for its Finalize() to be called. After all, Dispose() should have already taken care of its resources, both managed and unmanaged. What would Finalize() need to do in this case?

.NET provides a mechanism (GC.SuppressFinalize()), which tells the CLR not to invoke Finalize() on an object. Calling this method eliminates the overhead of the Finalize() call and the accompanying delay in reclaiming memory.

Let’s explore the usage of GC.SuppressFinalize() by studying Example 5-6.

Example 5-6. Suppressing the Finalize() call

C# (SuppressingFinalize)

using System;

namespace SuppressFinalize
{
    public class Test : IDisposable
    {
        private readonly int id;

        public Test(int theID)
        {
            id = theID;
        }

        ~Test()
        {
    Console.WriteLine("Finalize called on {0}", id);
        }

        #region IDisposable Members

        public void Dispose()
        {
    Console.WriteLine("Dispose called on {0}", id);
               GC.SuppressFinalize(this);
        }

        #endregion

        [STAThread]
        static void Main(string[] args)
        {
            int count = 1;
    Test object1 = new Test(count++);
               Test object2 = new Test(count++);

               object1.Dispose();

            Console.WriteLine("Main done");
        }
    }
}

VB.NET (SuppressingFinalize)

Public Class Test
    Implements IDisposable

    Private ReadOnly id As Integer

    Public Sub New(ByVal theID As Integer)
        id = theID
    End Sub


    Protected Overrides Sub Finalize()

        MyBase.Finalize()
        Console.WriteLine("Finalize called on {0}", id)
    End Sub

    Public Sub Dispose() Implements System.IDisposable.Dispose
        Console.WriteLine("Dispose called on {0}", id)
        GC.SuppressFinalize(Me)
    End Sub

    Public Shared Sub Main()
        Dim count As Integer = 1
        Dim object1 As New Test(count)
        count += 1
        Dim object2 As New Test(count)
               object1.Dispose()

        Console.WriteLine("Main done")
    End Sub
End Class

In this example, the class Test implements the IDisposable interface. Its Dispose() method invokes GC.SuppressFinalize(). In Main(), you create two objects of Test and dispose of only one of them. When you execute the code, you get the output shown in Figure 5-6.

Output from Example 5-6

Figure 5-6. Output from Example 5-6

Note that Finalize() gets called on the second object, but not on the first. When you invoke Dispose() on the first object, you indicate to the CLR that it should not bother invoking Finalize() on this object. As a result, the memory used by the object gets released without the added delay involved with Finalize().

IN A NUTSHELL

Calling Finalize() on an object involves overhead. This should be avoided as much as possible. From within your Dispose() method, call GC.SuppressFinalize().

GOTCHA #40 Implementing IDisposable isn’t enough

Let’s review key points from Gotcha #35, Gotcha #38, and Gotcha #39:

  • The call to Finalize() involves overhead, which you can avoid by calling GC.SuppressFinalize() in Dispose().

  • There is no guarantee when the Finalize() method will be called, so you may hold resources much longer than you intend.

For these reasons, it is better to use Dispose() instead of relying on Finalize().

That sounds good, except for one problem. Unfortunately, the author of a class has no way to force the user of an object to invoke the Dispose() method. It requires discipline on the part of the programmer to call Dispose() at the appropriate location in the code. What if he forgets to? You want the resources to be cleaned up anyway; doing it in Finalize() is better than not doing it at all. You know that Finalize() will be called eventually, you just don’t know when.

So what code should you put in Finalize()? Remember that you should not touch any managed resources (Gotcha #36, "Releasing managed resources in Finalize() can wreak havoc“). But you do need to clean up unmanaged resources.

What code do you write in Dispose()? If you call GC.SuppressFinalize() in Dispose() (as you should), you won’t be able to rely on Finalize() to clean up unmanaged resources; Dispose() will have to take care of them.

In addition, since Dispose() (unlike Finalize()) is not executed in the middle of a garbage-collection sweep, it can safely release other managed objects. So within Dispose() you take care of cleaning up both managed and unmanaged resources.

How do you free unmanaged resources in Dispose() and Finalize() without duplicating code? In Gotcha #38, "Depending on Finalize() can tie up critical resources," these two methods made identical calls to ReleaseComObject() (see Example 5-5).

The Dispose design pattern will help properly refactor Dispose() and Finalize(). (Refer to “Dispose Design Pattern” in the section "on the web" in the Appendix.)

This pattern stipulates that both Finalize() and IDisposable.Dispose() delegate to a third protected virtual/Overridable method. According to Microsoft’s specification, this method should be called Dispose() as well, and should take one bool/Boolean parameter, which Microsoft calls disposing. This indicates whether the method has been called by IDisposable.Dispose() (true) or Finalize() (false). This protected Dispose() method does all resource cleanup. Whether the disposing argument is true or false, it cleans up unmanaged resources. But only if this argument is true, indicating that the object is still accessible, does it attend to managed resources.

The Dispose(bool disposing) method is protected because you don’t want users of your class to call it directly, and you need to override it in any derived classes.

Tip

A better name could have been chosen for the Dispose(bool) method in the Dispose Design Pattern. It is less confusing if you can clearly see the difference between the methods named Dispose. Unfortunately, a number of classes in the .NET class library (e.g., System.EnterpriseServices.ServicedComponent), expect you to use the Dispose(bool) method. I’m sticking with the method names in the pattern to be consistent.

With this pattern, only classes at the top of your class hierarchies need to implement Finalize() and IDisposable.Dispose(). Those two methods call Dispose(false) and Dispose(true), respectively. Derived classes just implement the protected version of Dispose(). Because this method is defined as virtual/Overridable in the base class, and override/Overrides in all derived classes, the correct derived implementation will execute.

Tip

The full VB.NET prototype also includes the Overloads keyword, as follows:

   Protected Overloads Overrides Sub Dispose( _
       ByVal disposing As Boolean)

To complete the pattern, the derived class’s Dispose() must invoke its base class’s Dispose(); i.e., the protected version with the disposing parameter.

Example 5-7 and Example 5-8 illustrate the Dispose Design Pattern.

Example 5-7. Dispose design pattern (C#)

C# (DisposeFinalize)

//Base.cs
using System;

public class Base : IDisposable
{
    private bool disposed = false;
    private readonly int id;

    public Base(int theID)
    {
        id = theID;
    }

    protected virtual void Dispose(bool disposing)

    {
        if (!disposed)
        {
    if (disposing)
            {
                Console.WriteLine(
                    "Base Cleaning up managed resources on {0}",
                    id);

        // Code to clean up managed resources
            }

            Console.WriteLine(
                "Base Cleaning up unmanaged resources on {0}", id);

    // Code to clean up unmanaged resources
        }

        disposed = true;
    }

    public void Dispose()

    {
        Dispose(true);
               GC.SuppressFinalize(this);
    }

    ~Base()
    {
        Console.WriteLine("*** Finalize called on Base {0}", id);
        Dispose(false);
    }
}

//Derived.cs
using System;

namespace DisposePattern
{
    public class Derived : Base
    {
        private bool disposed = false;

        public Derived(int theID) : base(theID) {}

        protected override void Dispose(bool disposing)
        {
            if (!disposed)
            {
                try
                {
            if (disposing)
                    {
                        Console.WriteLine(
                            "Derived Cleaning up managed resources");
                // Code to clean up managed resources
                    }

                    Console.WriteLine(
                        "Derived Cleaning up unmanaged resources");
            // Code to clean up unmanaged resources
                }
                finally
                {
            base.Dispose(disposing);
                }
            }

            disposed = true;
        }
    }
}

//Test.cs
using System;

namespace DisposePattern
{
    class Test
    {
        [STAThread]
        static void Main(string[] args)
        {
            Derived object1 = new Derived(1);
            Derived object2 = new Derived(2);

            object1.Dispose();
        }
    }
}

Example 5-8. Dispose design pattern (VB.NET)

VB.NET (DisposeFinalize)

'Base.vb

Public Class Base
    Implements IDisposable
    Private disposed As Boolean = False
    Private ReadOnly id As Integer

    Public Sub New(ByVal theID As Integer)
        id = theID
    End Sub

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)

        If Not disposed Then
    If disposing Then
                Console.WriteLine( _
                    "Base Cleaning up managed resources on {0}", _
                    id)
 
        ' Code to cleanup managed resources
            End If
            Console.WriteLine( _
             "Base Cleaning up unmanaged resources on {0}", id)

    ' Code to cleanup unmanaged resources

        End If

        disposed = True
    End Sub

    Public Sub Dispose() Implements System.IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overrides Sub Finalize()

        MyBase.Finalize()
        Console.WriteLine("*** Finalize called on Base {0}", id)
        Dispose(False)
    End Sub
End Class

'Derived.vb
Public Class Derived
    Inherits Base

    Private disposed As Boolean = False

    Public Sub New(ByVal theID As Integer)
        MyBase.New(theID)
    End Sub


    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

        Try
            If Not disposed Then

                If disposing Then
                    Console.WriteLine( _
                        "Derived Cleaning up managed resources")
            ' Code to cleanup managed resources
                End If

                Console.WriteLine( _
                    "Derived Cleaning up unmanaged resources")
        ' Code to cleanup unmanaged resources
            End If

            disposed = True
        Finally
    MyBase.Dispose(disposing)
        End Try
    End Sub
End Class

'Test.vb
Module Module1

    Sub Main()
        Dim object1 As New Derived(1)
        Dim object2 As New Derived(2)

        object1.Dispose()
    End Sub

End Module

Take a look at the Base class. It implements IDisposable, so it must provide a Dispose() method with no parameter. This method invokes the Dispose(bool disposing) method with true as its argument. The Finalize() method invokes Dispose(bool disposing) as well, but passes false instead of true. The Dispose(bool disposing) method cleans up unmanaged resources regardless of the parameter’s value. It only cleans up managed resources if disposing is true, that is, only if it’s been called by the no-parameter Dispose() method. If invoked by the Finalize() method, it does not touch any managed resources. See Gotcha #36, "Releasing managed resources in Finalize() can wreak havoc."

In the Derived class, you do not have to implement the Finalize() method or the no-parameter Dispose(). Instead, you simply override the Dispose() method that takes the bool/Boolean parameter. First you clean up managed resources, but only if the disposing parameter is true. Next, you take care of unmanaged resources. Finally, you call the corresponding Dispose() method of the Base class. The output from the above program is shown in Figure 5-7.

Output from Example 5-7

Figure 5-7. Output from Example 5-7

From the output, you can see that object1 is cleaned up using the Dispose() method and that its Finalize() is not called. Both managed and unmanaged resources are cleaned up properly here. On the other hand, object2 is cleaned up in the Finalize() method. Only the unmanaged resources are deleted in this case, as expected.

With this code, you still need to be concerned about a couple of problems. First, you may have to make some adjustments to the Dispose(bool) method for thread-safety in a multithreaded application. Second, it is possible for a user to call the Dispose() method on an object more than once, and to call other methods on an object after the call to Dispose(). In your methods, you need to check if the object has been disposed. If it has, you should raise an ObjectDisposedException.

As a final illustration of the Dispose Design Pattern, let’s revisit the Wrapper class from Gotcha #38, "Depending on Finalize() can tie up critical resources" (see Example 5-5). In the previous version, both Finalize() and Dispose() call ReleaseComObject(). By moving this call to Dispose(bool disposing), you remove the duplication. The Wrapper class in this example has no managed resources and no base class below System.Object. Therefore, it does not call base.Dispose().

Example 5-9 shows the refactored implementation.

Example 5-9. Refactored Wrapper Class

C# (RefactoredWrapper)

//Wrapper.cs
using System;
using ACOMCompLib;

namespace FinalizePeril
{
    public class Wrapper : IDisposable
    {
        IMyComp comp = new MyCompClass();
        bool disposed = false;

        public int doSomething()
        {
            if (disposed)
            {
               throw new ObjectDisposedException(null);
            }
            int result;
            comp.doSomething(out result);
            return result;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
               // No managed resources to clean up
               // Clean up unmanaged resources regardless of who called us
               System.Runtime.InteropServices.Marshal.ReleaseComObject(comp);
               GC.SuppressFinalize(this);
               disposed = true;
            }
        }

        ~Wrapper()
        {
    Dispose(false);
        }

        #region IDisposable Members

        public void Dispose()
        {
    Dispose(true);
        }

        #endregion
    }
}

VB.NET (RefactoredWrapper)

'Wrapper.vb

Imports ACOMCompLib

Public Class Wrapper
       Implements IDisposable
    Dim comp As IMyComp = New MyCompClass
    Private disposed As Boolean = False


    Public Function doSomething() As Integer
        If disposed = True Then
           Throw New ObjectDisposedException(Nothing)
        End If
        Dim result As Integer
        comp.doSomething(result)
        Return result
    End Function

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not disposed Then
           ' No managed resources to clean up
           ' Clean up unmanaged resources regardless of who called us
           System.Runtime.InteropServices.Marshal.ReleaseComObject(comp)
           GC.SuppressFinalize(Me)
           disposed = True
        End If
    End Sub


    Public Sub Dispose() Implements System.IDisposable.Dispose
        Dispose(True)
    End Sub

    Protected Overrides Sub Finalize()
        Try
           Dispose(False)
        Finally
           MyBase.Finalize()
        End Try
    End Sub

End Class

IN A NUTSHELL

Follow the Dispose design pattern (with adjustments for thread-safety if needed), because it provides an effective way to clean up both managed and unmanaged resources.

GOTCHA #41 Using the Dispose Design Pattern doesn’t guarantee cleanup

While the IDisposable interface and the Dispose Design Pattern are very important to follow, these are recommendations on how you write a class, not how you use an instance of the class. If you create an object, the CLR may eventually call its Finalize(). On the other hand, Dispose() is never called automatically. Users of your object have the responsibility of calling it. What if they forget to?

The problem does not end there. Even if you have a call to Dispose(), what if an exception is thrown before the program reaches that statement? What can you do to ensure that Dispose() is called and thereby increase the probability of proper cleanup?

To deal with these issues, C# offers the using keyword (though there is no way to force the user to use it). Consider Example 5-10.

Example 5-10. Code that utilizes the using keyword

C# (Using)

//Test.cs
using System;

namespace DisposePattern
{
    class Test
    {
        [STAThread]
        static void Main(string[] args)
        {
            using(Derived object1 = new Derived(1))
            {
                Derived object2 = new Derived(2);
            }
        }
    }
}

In this example, you use the using keyword when creating object1 of the Derived class. You do not call its Dispose() method. (The class Derived is the same as the one used in Example 5-7.) The using keyword provides a significant benefit. It not only makes the call to Dispose(), it does so in a finally block, as the generated MSIL in Figure 5-8 shows. This ensures that the object is disposed of properly even when an exception is thrown.

MSIL generated for Main() in Example 5-10

Figure 5-8. MSIL generated for Main() in Example 5-10

You can declare more than one reference in the using declaration:

using(Derived obj1 = new Derived(1), obj2 = new Derived(2))

The Dispose() method, in this case, will be called on each referenced object at the appropriate time.

Unfortunately, using is only available in C#. In VB.NET, to make sure Dispose() is invoked, call it in a Finally block. A VB.NET example of calling Dispose() in the Finally block is shown below:

            Try
                theWrapper = New Wrapper
                result = theWrapper.doSomething()
            Finally
                theWrapper.Dispose()
            End Try

How does this differ in .NET 2.0 Beta 1? A Using...End Using block is being introduced in the next version of VB.NET.

IN A NUTSHELL

The using keyword in C# improves your chances of cleaning up your resources properly. In VB.NET, you have to take responsibility for invoking Dispose() by calling it within a Finally block. Take advantage of the Using...End Using block when you move to .NET 2.0.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.70.113