Do Anonymous Methods Prevent Declaring Types from Being GC’d?


It seems that they do if they reference members of the declaring type. This makes perfect sense now that I think about it, but I didn’t think about it earlier and wrote some code that caused a memory leak. I had an object acting as a singleton and referencing a delegate instance that was created from an object acting as a non singleton. Bam! Memory Leak.

I setup a little test to demonstrate. Here is a class that has two methods which return delegate instances:

public class TestSource
{
    private string internalValue = "test";
<span style="color: #0000ff">public</span> Func&lt;<span style="color: #0000ff">bool</span>&gt; GetFunc1()
{
    <span style="color: #0000ff">return</span> () =&gt; 1 == 1;
}

<span style="color: #0000ff">public</span> Func&lt;<span style="color: #0000ff">string</span>&gt; GetFunc2()
{
    <span style="color: #0000ff">return</span> () =&gt; internalValue;
}

}

Notice that GetFunc1() doesn’t have any references to internal members, but GetFunc2() does. Here’s the test class:

class Program
{
    static void Main(string[] args)
    {
        // hold no references to TestSource
        TestFuncInstance(new TestSource().GetFunc1());
        TestFuncInstance(new TestSource().GetFunc2());
    Console.WriteLine(<span style="color: #006080">"Press Enter to continue."</span>);
    Console.ReadLine();
}

<span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> TestFuncInstance(Delegate func)
{
    Thread.Sleep(1000);<span style="color: #008000">// give some time for GC</span>

    Console.WriteLine(<span style="color: #0000ff">string</span>.Format(<span style="color: #006080">"Method: {0}"</span>,func.Method));
    Console.WriteLine(<span style="color: #0000ff">string</span>.Format(<span style="color: #006080">"DeclaringType: {0}"</span>, func.Method.DeclaringType));
    Console.WriteLine(<span style="color: #0000ff">string</span>.Format(<span style="color: #006080">"Target: {0}"</span>, func.Target ?? <span style="color: #006080">"null"</span>));

    Console.WriteLine();
}

}

This creates two separate instances of TestSource and passes the result of the two GetFunc methods to a test method. Notice that there are no declared variable references to the TestSource object. Here’s the output of the test:

Method: Boolean <GetFunc1>b__0()
DeclaringType: TestingDelegates.TestSource
Target: null 

Method: System.String <GetFunc2>b__2() DeclaringType: TestingDelegates.TestSource Target: TestingDelegates.TestSource

Press Enter to continue.

I know this test isn’t very scientific, but you’ll see that Func1’s target is null while Func2’s target is not. Func2 has to hold a reference to the declaring object so that it can do it’s job when invoked. Func1 does not need a reference, and seems to free up the declaring object to be garbage collected. This is definitely something to keep in mind when passing around delegates.

Technorati Tags: ,,
← Synchronizing UI Operations with Asynchronous Dependencies
From Flex to Silverlight →