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";

    public Func<bool> GetFunc1()
    {
        return () => 1 == 1;
    }

    public Func<string> GetFunc2()
    {
        return () => 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("Press Enter to continue.");
        Console.ReadLine();
    }

    private static void TestFuncInstance(Delegate func)
    {
        Thread.Sleep(1000);// give some time for GC

        Console.WriteLine(string.Format("Method: {0}",func.Method));
        Console.WriteLine(string.Format("DeclaringType: {0}", func.Method.DeclaringType));
        Console.WriteLine(string.Format("Target: {0}", func.Target ?? "null"));

        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: ,,

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Ray Houston

Ray is a software development leader and architect with 20 years hands-on experience. He enjoys finding elegant solutions to complex problems and delivering real value to customers. Ray is a speaker and contributor to community events.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

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

  1. Jimmy Bogard says:

    Hmmm…I think this has something to do with Closures.

  2. Jeremy Wiebe says:

    I _believe_ that if you look at your code in Reflector you’d see that the anonymous delegate actually becomes a method on an inner class of your declaring type. This would make sense then that the declaring class is not GC’d.

    One side note, I think the test would be more explicit if you forced a GC instead of doing a Thread.Sleep(). My $0.02. :-)

  3. It’s not actually a memory leak. It’s because the lambda returned from GetFunc2 refers to a field of the outer object. That field then needs to be lifted (see: lambda lifting).

    When this happens, it’s as if a LineItem object was given an implicit reference to its Order object and then the LineItem got passed somewhere else.

    As long as you have an uncollected instance of the lambda (which is a closure) that refers to data from outside it’s own scope (the instance filed), that outer scope is kept alive so that the lambda can continue to have access to its lifted data.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>