Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Action, Closure, and Garbage Collection

Do I need to set MyAction to null so that garbage collection will be able to proceed with either of these classes?

I am less concerned when both classes are to have almost the same lifespan. My question is more appropriate when Class1’s lifespan is much longer then Class2 or when Class2’s lifespan is much longer then Class1.

The code here is stripped down. Assume that both Class1 and Class2 contain other members and methods that may affect their lifespan.

public class Class1 : IDisposable
{
    public Action<string> MyAction { get; set; }

    // Is this necessary?
    public void Dispose()
    {
        MyAction = null;
    }
}

public class Class2
{
    string _result = string.Empty;

    public void DoSomething()
    {
        Class1 myClass1 = new Class1();
        myClass1.MyAction = s => _result = s;
        myClass1.Dispose();
    }
}
like image 302
Paul Matovich Avatar asked Nov 17 '11 16:11

Paul Matovich


2 Answers

Do I need to set MyAction to null so that garbage collection will be able to proceed with either of these classes?

No. Any time you "Dispose" of a managed resource, odds are good you are doing it wrong. Let the garbage collector do its work.

I am less concerned when both classes are to have almost the same lifespan. My question is more appropriate when Class1’s lifespan is much longer then Class2 or when Class2’s lifespan is much longer then Class1.

The question doesn't make any sense; classes do not have lifetimes. Storage locations have lifetimes. Precisely which storage locations are you worried about? Can you clarify the question?

I note that there are very serious concerns about extending lifetimes of closed-over variables via closures; your question however is so vague that it is hard to understand whether you are running into such a situation. Let me demonstrate:

class Expensive
{
    public byte[] huge = MakeHugeByteArray();
}

class Cheap
{
    public int tiny;
}

class C
{
    public static Func<Cheap> longLived;
    public static void M()
    { 
        Expensive expensiveLocal = new Expensive();
        Cheap cheapLocal = new Cheap();
        Func<Expensive> shortLived = ()=>expensiveLocal ;
        C.longLived = ()=>cheapLocal;
    }
}

What is the lifetime of local variable expensiveLocal? The lifetime of a local variable is typically short; typically local variables live no longer than the method activation. However, being in a closure extends the lifetime of a local variable arbitrarily long, to the lifetime of the closure. In this particular case, both lambdas share a closure, and that means that the lifetime of local variable expensiveLocal is at least as long as the lifetime of local variable cheapLocal, which has an indefinitely long lifetime because a reference to the closure has just been stored in a static field that lives forever. That big byte array might never be reclaimed, even though the only thing that seems to reference it has been collected long ago; the closure is a hidden reference.

Many languages have this problem; C#, VB, JScript and so on all have lexical closures that are not partitioned to group variables by lifetime. We are considering changing C# and VB to have better lifetime management for closures but that is far-in-the-future work at this point, so no guarantees.

like image 186
Eric Lippert Avatar answered Oct 21 '22 08:10

Eric Lippert


No. There's no need to set the reference to null.

Dispose() is for cleaning up un-managed resources. Action<string> is a manged resource and will be properly handled by the CLR when the instance of Class1 falls out of scope at the end of DoSomething().

like image 37
Justin Niessner Avatar answered Oct 21 '22 10:10

Justin Niessner