Heisenberg And Lambdas

Writing

Today on Twitter Pete Brown mentioned that he had put up a post about using lambdas to set events in Silverlight. It is a really clean pattern, and it is useful in most places that you need to set events (this is a stripped down version of the code that Pete posted on his blog):

public class Test
{        
    public void LoadPolicyDetail()
    {
        var client = new IvcDataServiceClient();

        client.GetPolicyDetailCompleted += (s, e) =>
        {
            // Do something
        };

        client.GetPolicyDetailAsync();
    }
}

In this code you can see that we are creating a client class and then assigning a statement lambda to the event. The code inside of the event isn’t really important, and so I just removed it. Then we call “GetPolicyDetailsAsync” which will make an asynchronous call which when it completes invokes the “GetPolicyDetailCompleted” event.

Will It Leak?

The question is, since the method leaves asynchronously whether or not there are any resources leaked or there is any chance of something being garbage collected too early since the client and lambda are both going out of scope as soon as the method exits.

In order to get an answer to this we are going to have to consider how the lambda in this method is going to be generated. Since this lambda isn’t leaving the scope of this method, the C# compiler is going to emit it as a private static method on the test class like this:

[CompilerGenerated]
private static void <LoadPolicyDetail>b__0(object s, EventArgsSpecial e)
{
}

So all we are really doing is assigning this static method to the event handler. Since we are assigning this static method to the event handler then it doesn’t really matter what happens to the current instance of this class…but hold that thought. Let’s look at the program which runs this code:

class Program
{
    static void Main(string[] args)
    {
        var test = new Test();            
        test.LoadPolicyDetail();            
        Console.ReadLine();
    }
}

So we create the Test object call the method on it, and then just wait until the user hits a button in order to exit the program. So as you can see the “test” class is going to be garbage collected right after the “LoadPolicyDetail” method is called, but since the event was on the static method it will still be called.

Now the client class is a bit different, it doesn’t get garbage collected. But how? Doesn’t it go out of scope when we leave the “LoadPolicyDetail” method? Let’s look at how we implemented that class:

public class IvcDataServiceClient
{
    public event Action<Object, EventArgsSpecial> GetPolicyDetailCompleted;

    public void GetPolicyDetailAsync()
    {
        ThreadPool.QueueUserWorkItem(_ =>
        {
            Thread.Sleep(5000);
            GetPolicyDetailCompleted(this, new EventArgsSpecial());
            Console.WriteLine("Event Finished");
        });
    }
}

So, yes, the variable goes out of scope, but a reference is still held to the class by the thread that was spun up in the thread pool. This is because if you notice this lambda is referencing the instance by using “this” and “GetPolicyDetailCompleted”, and so it is created as an instance method on the “IvcDataServiceClient” class. Aaaaaaaah. Are you lost yet?

In the end it kinda feels like you are trying to stitch together the compiler magic in your head just to see if something is going to be kept alive or not. But how can we tell if something has been garbage collected? If we have a reference to it, then we know it won’t be collected. I bet you were wondering where Heisenberg came into this!

WeakReference To The Rescue

Well, it is a good thing that the .NET Framework team saw fit to bless us with the WeakReference class. This wonderful little class allows us to get a reference to an object which is “weak”, meaning that you can hold a reference but it won’t be counted as a reference by the garbage collector. This class has a property on it called “IsAlive” which returns a boolean that says whether or not the instance had been garbage collected.

So how can we use this to our advantage? Well, we can get a weak reference to the class that we expect to be collected, and then force a garbage collection. If the class is collected, then we know that we don’t have a leak. So in this case we could do this:

public class Test
{        
    public WeakReference LoadPolicyDetail()
    {
        var client = new IvcDataServiceClient();
        var weakReference = new WeakReference(client);

        client.GetPolicyDetailCompleted += (s, e) =>
        {
            // Do something
        };

        client.GetPolicyDetailAsync();
        return weakReference;            
    }
}

And so now we are going to return a weak reference to the client class, and in the calling program we can check this:

class Program
{
    static void Main(string[] args)
    {
        var test = new Test();            
        WeakReference wr = test.LoadPolicyDetail();            
        
        GC.Collect();
        Console.WriteLine(wr.IsAlive);
        Console.ReadLine();
        
        GC.Collect();
        Console.WriteLine(wr.IsAlive);
        Console.ReadLine();
    }
}

We get the weak reference and then force a collection, then we check to see if the client is still alive. Which of course it is. Then we pause until the user hits enter. We can then wait for the event to finish and we will see the “Event Finished” message appear. If all went well then we can force another collection and then “IsAlive” should return false. In this case, it does, and we can now know that what we are doing will not leak memory. If we got a true back, then that would mean that even though the event finished and we have no more references to the objects, somehow the instance survived a garbage collection. Time to start looking into why that is happening. And when that happens, you might need the big guns like WinDbg and SOS.

Summary

WeakReference provides a fairly low level way of determining if something is leaking. It is also a way in which you can reference other classes without actually holding a strong references to them. This is actually quite useful in a number of different instances. I hope that you found this post interesting, and maybe even added another simple tool to your tool chest.

More Insights

View All