CLR Inside Out

Managing Object Lifetime

Tim Fischer

Contents

Garbage Collection and Finalization
Lifetime Management in COM
.NET Deterministic Finalization
Finalization in a COM Library
Memory Leaks

Although the Microsoft® .NET Framework provides a managed execution environment, it is important to use resources carefully, consider lifetime management, and write code that addresses these concerns correctly. In this column, I have set out to help developers who are consuming .NET or COM classes to better understand lifetime management, recognize situations where you can run into resource traps, and know how to solve these issues.

Note that, for clarity, the sample code shown in the column only works for single-threaded applications. Reusable thread-safe code for .NET resource management is available in the LifeTimeScope.net project on CodePlex (see codeplex.com/lifetimescope).

In .NET programming, an object is created with the new keyword and allocated on the managed heap. The object is alive until the garbage collector finds out that the object is no longer reachable from a strong root reference via a path of strong references. Each program has a main method and an associated class as well as static classes that all might contain local variables, static members, or even events. These static or local references are considered to be root references within your program (see Figure 1). Ordinary .NET Framework references are known to be strong references. Object lifetime is determined by the path of strong references that point from a root reference to an object.

Figure 1 Strong References to Objects on the Heap

Figure 1** Strong References to Objects on the Heap **

In some cases, such as when building a fast cache or a hash into an object graph, it might be interesting to have additional references to objects that do not change the lifetime of an object. For this purpose the .NET Framework offers an additional class called WeakReference that allows you to implement a weak reference between objects. This weak reference is ignored when determining the lifetime of an object.

Events can also be strong root references and as such can contribute to the strong reference path and thus affect the lifetime of an object. Ordinary events in the common language runtime (CLR) 2.0 are bidirectional strong references between the event source and the listener and as such can keep an object (either source or listener) alive that otherwise should be dead already. This is why a WeakEvent class was added in the .NET Framework 3.0. It is important that you become familiar with the WeakEvent pattern, which is not yet well known, but is required to implement the Observer pattern successfully. The WeakEvent pattern has been used in the Windows® Presentation Foundation (WPF) Data Binding implementation to prevent leakage due to data binding.

Garbage Collection and Finalization

Let's look at how the CLR determines whether an object is still alive and what happens when it isn't. In the November 2000 issue of MSDN® Magazine (msdn.microsoft.com/msdnmag/issues/1100/GCI), Jeffrey Richter describes the procedure. Each .NET process runs a separate thread that is used by the garbage collector. When the garbage collector runs, all other threads are paused. The garbage collector analyzes the memory structures and references to find out which objects are dead. These objects are then queued for a process called finalization. In a second phase, the finalization process is executed on the separate thread used by the garbage collector and the Finalizer method is called on each object.

There are two important points to note in this process. First, the Finalizer is called from a different thread than the thread that created the object. Second, the Finalizer is called at some point in time but not necessarily close to the time the object actually went dead (this can be noticed when the system is stressed, but memory is available).

The garbage collector is exposed in the static System.GC class. You can force garbage collection to run and let it identify the dead objects and queue them for finalization by calling GC.Collect. You can also force finalization to happen for all queued objects by calling GC.WaitForPendingFinalizers. However, there are two issues with calling WaitForPendingFinalizers: this is a time-consuming process and the Finalizers are called from another thread. This can lead to performance and threading issues if, for example, you need to release a reference to a system resource and plan to do this in your Finalizer—some unmanaged resources are thread-dependent. (In that case, please read on and learn about the Dispose pattern.)

As the .NET Framework team already identified the need for a timely destruction of objects and also the need for allowing the destruction code to be executed on a specific thread, they came up with a solution known as the Dispose pattern. For details, see the CLR Inside Out column in the July 2007 issue of MSDN Magazine at msdn.microsoft.com/msdnmag/issues/07/07/CLRInsideOut.

The Dispose pattern is a convention that should be implemented by every object that has resources and needs timely destruction. It is accomplished by implementing the IDisposable interface and releasing resources in the implementation of a method called Dispose exposed by the interface. However, just like in the unmanaged world, it is still the burden of the programmer to make sure the Dispose method is called at the right time in the client code. Brian Harry has a posting on his public forum that explains why the team came up with this solution and why other interesting solutions could not be applied successfully (see discuss.develop.com/archives/wa.exe?A2=ind0010A&L=DOTNET&P=R28572). The Dispose pattern must be correctly implemented for all objects that own resources.

Lifetime Management in COM

Lifetime Management Resources

In the COM world, lifetime management works via reference counting. Each COM object has an internal count of client references that point to it. These counters are incremented and decremented by the client developer by calling IUnknown::AddRef and IUnknown::Release. Note that AddRef increases the counter and should be called when a reference to the component is set while Release decreases the counter and should be called when the reference has been destroyed. When the counter goes back to 0, the object can be destroyed.

The calls to AddRef and Release were eased by the use of SmartPointers in Active Template Library (ATL) or by generating the necessary code by the compiler/runtime such as in Visual Basic® 6.0. Events were only strong pointers in one direction and weak pointers in the other direction.

The two major issues with reference counting are performance loss and the potential to be unable to detect dead objects that are part of a cyclic-reference scenario. The performance loss is generally acceptable in COM because it's typically used as a technology to combine larger components. The .NET Framework, however, is used on the class level and thus the performance impact would be too great if reference counting were added to the CLR.

If you use a COM component from managed code, you use tlbimp.exe to generate a so-called runtime callable wrapper (RCW), which exposes the functionality of the COM component to the managed world. Note that tlbimp.exe is executed in the background when you reference a COM component in Visual Studio®. You can easily identify RCWs as they are displayed as System.__ComObject in the debugger. They are .NET types that hold one unmanaged pointer to the COM object and call AddRef and Release on the object. AddRef is called once the component is created and Release is called when the RCW is finalized.

But when is the RCW finalized? You do not know, so you need a method to call Dispose on the RCW. Unfortunately, the generated RCWs do not implement IDisposable. Currently, the equivalent to calling the missing Dispose method is to call:

System.Runtime.InteropServices.Marshal.ReleaseComObject(MyCOMObject)

.NET Deterministic Finalization

In many cases you need to make sure that the resources you use are released as soon as possible. The ideal way is through the using keyword in C# to ensure that the Dispose method is called on the object once program flow leaves the scope indicated by the curly brackets:

using (FileStream theFileStream = File.Create("C:\\hello.txt"))
{
    string s = theFileStream.Name; 
}

In addition, it's worth noting that C++/CLI automatically calls the destructor of local variables when exiting a method. In these scenarios, you can generally assume that the Dispose method is called automatically at the end of the block or method.

However, these approaches do not work when the usage scope of the resource is not local. Assume you call another method in the preceding code that uses the FileStream object as an input parameter. That method may not call Dispose or use the using keyword. In the context of a multithreaded server sharing resources between threads, this might become even more complex.

Unfortunately C# does not provide a simple construct equivalent to the using keyword that can be used across scopes or threads. This makes reusing code and routines difficult as the responsibility of calling Dispose must be negotiated between components to ensure it is done only once, and at the right time.

Beware: if you do not call Dispose explicitly, then the Finalizer will do it for sure if the object implements the Dispose pattern properly. However, you risk running out of resources under high loads because you do not know when the Finalizer will be called. You also risk having the Finalizer fail when it is called from another thread than the one that created the object. This can become an issue if you try to release unmanaged resources or COM resources in a Finalizer.

One possible solution is to build a generic wrapper class around any type implementing IDisposable. The wrapper class returns an IDisposable interface, but internally sets a reference counter before delegating the call to the component. The code shown in Figure 2 uses a new extension method, called AddRef, to get a corresponding wrapper object that contains an internal reference counter for a specific object.

Figure 2 An IDisposable Wrapper

static void Main(string[] args)
{
    FileStream theFileStream = File.Create("C:\\hello.txt");
    // Unscoped use of RefCounted
    using (theFileStream.AddRef())
    {
        a(theFileStream);
    }
          
    System.Console.Read();
}

static void a(FileStream inFileStream)
{
    // Unscoped use of RefCounted
    using (inFileStream.AddRef())
    {
        // do something
        string s = inFileStream.Name;
    }
}

When AddRef is called, the code looks into a hash table to see if the object already has a wrapper. If so, it returns the wrapper and increases the counter; otherwise it creates a new wrapper and adds it to the hash table. When the program reaches the end of the using block, Dispose is called automatically and it decrements the counter. In case the counter goes to zero, the Dispose call is propagated to the underlying object.

The implementation of the extension method and wrapper is shown in Figure 3. For clarity, this code is not designed to be thread safe.

Figure 3 AddRef Extension and Wrapper

public static class ReferenceCountingExtensions
{
    public static System.Collections.Generic.Dictionary<object,object> 
        theHashOfReferenceCountedClasses = 
        new System.Collections.Generic.Dictionary<object,object>();
    public static RefCounted<T> AddRef<T>(this T resource) 
        where T : IDisposable
    {
        object theObject=null;
        theHashOfReferenceCountedClasses.TryGetValue(resource, 
            out theObject);
        if (theObject == null)
        {
            theObject = new RefCounted<T>(resource);
            theHashOfReferenceCountedClasses[resource] = theObject;
        }
        else
        {
            ((RefCounted<T>)theObject).AddRef();
        }
        return (RefCounted<T>) theObject;
    }
}

// Base Class for a RefCountedUnmanagedResourceProtectingClass
public class RefCounted<T> : IDisposable
{
    private T theResource;
    bool IsFinalDisposed = false;
    int RefCounter = 0;
    public RefCounted(T inResource)
    {
        theResource = inResource;
        System.Threading.Interlocked.Increment(ref RefCounter);
    }
    public RefCounted<T> AddRef()
    {
        System.Threading.Interlocked.Increment(ref RefCounter);
        return this;
    }
    public T Resource
    {
        get
        {
            return theResource;
        }
    }
    ~RefCounted()
    {
        if (!IsFinalDisposed)
        {
            Dispose();
        }
    }
     public void Dispose()
    {
        System.Threading.Interlocked.Decrement(ref RefCounter);
        if ((RefCounter == 0) && (!IsFinalDisposed)) FinalDispose();
    }
    virtual public void FinalDispose()
    {
        ((IDisposable)theResource).Dispose();
        IsFinalDisposed = true;
    }
}

Make sure that your Finalizers really run to the end. The .NET Framework runtime provides a concept called Constrained Execution Regions and the CriticalFinalizerObject that can help you achieve this.

Finalization in a COM Library

Let's look at a scenario where you use a COM object model from C#. You can use any COM component easily by creating a reference to its registered type library. Let's assume you have a COM component exposing a telephone API (TAPI) and use it in the following lines of code—see if you can find a bug related to lifetime management here:

void foo (CTAPIApplication TAPIApplication){
    string s = TAPIApplication.Ports[0].SpeechListener.GetName();
}

The bug is that a Ports collection was acquired from the object model, but its ReleaseComObject method was not called. The same is true for SpeechListener. You may argue that it is not safe to assume that foo should release the ComObject, and you are right; it is not clear from this code who should release the object and when. So what can you do?

The solution is again to use the AddRef Method, but you need to modify the RefCounted class to understand that it must call ReleaseComObject in case it is a ComObject. Figure 4 shows the enhanced FinalDispose method. In addition, where T : IDisposable was removed from the call to AddRef<T> because there is no common interface that IDisposable and COM Objects share:

Figure 4 FinalDispose Method

virtual public void FinalDispose()
{
    if(theResource.GetType().IsCOMObject)
    {
        Marshal.ReleaseComObject(theResource);
    } 

    else 
    {               
        ((IDisposable)theResource).Dispose();
    }

    IsFinalDisposed = true;
}

public static RefCounted<T> AddRef<T>(this T resource)

Now you can rewrite the call into the telephone API as shown in Figure 5. While this code still is not very handy, it is very safe because the using statement ensures that ReleaseComObject really gets called even if an exception is thrown at some point. Adding another helper class called LifeTimeScope and changing the return type of AddRef makes the code even more readable:

Figure 5 Calling into COM

void foo(CTAPIApplication aTAPIApplication)
{
    using(PortsCollection aPortsCollection = 
        aTAPIApplication.Ports.AddRef())
    {
        using (Port aPort = aPortsCollection[0].AddRef())
        {
            using(SpeechListener aListener = 
                aPort.SpeechListener.AddRef())
            {
                string s  = aListener.GetName()
            }
        }
    }
}

void foo (CTAPIApplication TAPIApplication)
{
    using (new LifeTimeScope())
    {
        string s = 
            TAPIApplication.Ports.AddRef()[0].AddRef()
            .SpeechListener.AddRef().GetName();
    }
}

The LifeTimeScope class, shown in Figure 6, acts as a helper to call Dispose on all object wrappers that were touched by AddRef within a using block. AddRef got a few changes as well (see Figure 7). While these are all good changes, you still need to make the call to AddRef yourself.

Figure 7 Revised AddRef

public static class ReferenceCountingExtensions
{
    static public System.Collections.Generic.Dictionary<object, object>
        theHashOfReferenceCountedClasses = 
        new System.Collections.Generic.Dictionary<object, object>();

    public static T AddRef<T>(this T resource)
    {
        object theObject=null;
        theHashOfReferenceCountedClasses.TryGetValue(resource, 
            out theObject);
        if (theObject == null)
        {
            theObject = new RefCounted<T>(resource);
            theHashOfReferenceCountedClasses[resource] = theObject;
        }
        else
        {
            ((RefCounted<T>)theObject).AddRef();
        }
        System.LifeTimeScope.Current
            .theListOfWrapperObjectsToCallDisposeOn.Add(theObject);
        return (T) resource;
    }
}

Figure 6 LifeTimeScope

public  class LifeTimeScope : IDisposable 
{
    private static LifeTimeScope _Current = null;

    private static Stack<LifeTimeScope> theScopeStack = 
        new Stack<LifeTimeScope>(); 

    public static LifeTimeScope Current
    {
        get{ return _Current; }
    }
  
    public System.Collections.ArrayList 
        theListOfWrapperObjectsToCallDisposeOn = 
        new System.Collections.ArrayList();

    public LifeTimeScope()
    {
        _Current = this;
        theScopeStack.Push(this);
    }
  
    public void Dispose()
    {
        foreach (IDisposable theObject in 
            theListOfWrapperObjectsToCallDisposeOn)
        {
            theObject.Dispose();
        }
        _Current = theScopeStack.Pop();
    }
}

Memory Leaks

The most common issue you will run into is that your application leaks memory due to event subscriptions. For example, you could write a Windows Forms user control that subscribes to the NetworkChange event in its constructor like this:

NetworkChange.NetworkAvailabilityChanged += new 
    NetworkAvailabilityChangedEventHandler(ctrl_NetAvailChangedHandler);

The user control and the NetworkAvailabilityChanged static class hold bidirectionally strong references to each other. Because NetworkAvailabilityChanged is a static root reference, the user control will not get released from memory until the event handler is removed.

If you don't unsubscribe an event from an object that exceeds the lifetime of its referenced object—in this case NetworkAvailabilityChanged and its referenced user control—you automatically create a memory leak. This problem is a common one and applies to all sorts of Observer pattern scenarios, including data binding.

The .NET Framework 2.0 documentation says you should unsubscribe the event in the Dispose method of the object. It's also a good idea to make sure Dispose is called. That's because in the .NET Framework 2.0 world events are resources that you need to care about. However, in the .NET Framework 3.0 you can employ a solution based on the WeakEvent pattern.

Finally, another way to assure destruction of unmanaged resources is by using the HandleCollector class, which helps you run GC.Collect occasionally. Figure 8 provides an example Create an instance of HandleCollector by providing three parameters: Handle Name (string), Initial Threshold (int), and Maximum Threshold (int). The initial threshold is the point at which the garbage collector can start performing garbage collection. The maximum threshold is the point at which the garbage collector must perform garbage collection.

Figure 8 Using HandleCollector

// Handle Collector offers an easy way to have garbage collection
// started once we have 30+ active (and possibly destroyed) objects

class MyClassThatForcesGarbageCollectionIf30HandlesAreActive
{
    // Create a new HandleCollector
    static readonly HandleCollector appHandleCollector = 
        new HandleCollector("ApplicationUnmanagedHandles", 5, 30);

    public MyClassThatForcesGarbageCollectionIf30HandlesAreActive()
    {
        // Increment Handle Count
        appHandleCollector.Add();
    }
    ~MyClassThatForcesGarbageCollectionIf30HandlesAreActive()
    {
        // Decrement Handle Count
        appHandleCollector.Remove();
    }
}
}

This was a brief overview of lifetime management in .NET or COM classes. I hope you can now recognize and solve these issues. For more information, please visit the links in the "Lifetime Management Resources" sidebar.

Send your questions and comments to clrinout@microsoft.com.

Tim Fischer lives in Germany and is a Program Manager for Visual Studio and Expression products at Microsoft. He holds a degree in computer science from the University of Stuttgart and worked for 12 years as a consultant for model-driven development. He is the founder of tangible engineering, a company dedicated to building world-class .NET software factories.