简体   繁体   中英

C# Actions and GC

I am using Actions in C# and I was wondering if I need to set the instance of Action to null once I wish the GC to collect the objects properly? Here is an example:

public class A
{
 public Action a;
}

public class B
{
  public string str;
}

public class C
{
 public void DoSomething()
 {
   A aClass = new A();
   B bClass = new B();
   aClass.a = () => { bClass.str = "Hello"; }
 }
}

Inside my Main method I have something like this:

public void Main(...)
{
  C cClass = new C();
  cClass.DoSomething();

  Console.WriteLine("At this point I dont need object A or B anymore so I would like the GC to collect them automatically.");
  Console.WriteLine("Therefore I am giving GC time by letting my app sleep");
  Thread.Sleep(3000000);
  Console.WriteLine("The app was propably sleeping long enough for GC to have tried collecting objects at least once but I am not sure if A and B objects have really been collected");
 }
}

Please read the Console.WriteLine text it will help you understand what I am asking here.

If I apply my understanding of GC to this example the GC would never collect the objects since A cannot be destroyed because it holds instance of B. Am I right?

How can I properly collect those two objects? Do I need to set instances of Actions to null just to let GC collect objects before the end of application or is there already some kind of very smart mechanism by GC that knows how to destroy objects who have Actions such as A and B are?

EDIT: The question is about GC and collecting objects properly. Its not about calling the method collect().

There are numerous problems with this question. Rather than answer your question directly I'm going to answer the questions you should be asking.

Let's first disabuse you of your notions about the GC.

Will sleeping for a long time activate the garbage collector?

No.

What activates the garbage collector?

For testing purposes you can use GC.Collect() and GC.WaitForPendingFinalizers() . Use these only for testing purposes; it is a bad practice to use them in production code except in some very rare circumstances.

Under normal situations the things that trigger a GC are complicated; the GC is a highly tuned piece of machinery.

What are the semantics of garbage collection insofar as closed-over outer variables are concerned?

The lifetime of a closed-over outer variable of a lambda that is converted to a delegate is extended to be not shorter than the lifetime of the delegate .

Suppose I have a variable of type Action which is initialized with a lambda that is closed over an outer local variable of reference type. In order to make the object referred to by that variable eligable for collection, do I have to set the variable of type Action to null ?

In the vast majority of cases, no. The garbage collector is very smart; just let it do its work and do not worry about it . Eventually the runtime will determine that the Action variable cannot be reached by any live root and will make it eligable for collection; the closed-over outer variable will then become eligible.

There may be extremely rare situations in which you want to throw away references to the Action sooner, but they are rare; the vast majority of time, just let the GC do its job without interference.

Are there situations in which outer variables can have their lifetimes extended too long?

Yes. Consider:

void M()
{
    Expensive e = new Expensive();
    Cheap c = new Cheap();
    Q.longLived = ()=>c; // static field
    Q.shortLived = ()=>e; // static field
}

When M() is executed a closure is created for both delegates. Suppose that shortLived is going to be set to null soon, and longLived is set to null far in the future. Unfortunately both local variables have their lifetimes extended to the lifetime of the object referred to by longLived , even though only c is still reachable. The expensive resource e is not released until the reference in longLived is dead.

Numerous programming languages have this problem; some implementations of JavaScript, Visual Basic, and C# all have this problem. There is some talk of fixing it in the Roslyn release of C# / VB but I do not know if that will come to fruition.

In that case the solution is to avoid the situation in the first place; don't make two lambdas that share a closure if one of the delegates will live much longer than the other.

Under what circumstances does a local that is not a closed-over outer variable become eligable for collection?

The moment that the runtime can prove that a local cannot be read from again, the thing it references becomes eligable for collection (assuming the local is the only root of course.) In your example program there is no requirement that the references in aClass and bClass remain alive until the end of the method. In fact, there are rare but possible circumstances in which the GC can be deallocating an object on one thread while it is still in its constructor on another thread ! The GC can be very aggressive about determining what is dead, so be careful.

How do I keep something alive in the face of an aggressive GC?

GC.KeepAlive() of course.

I am using Actions in C# and I was wondering if I need to set the instance of Action to null once I wish the GC to collect the objects properly?

Not necessarily. As long as no object is reachable which references the delegate, the delegate will be eligible for GC.

That being said, in your example, aClass and bClass are still valid variables, and refer to reachable objects. This means that aClass.a is still reachable, and not eligible for GC, so it will not be collected.

If you wanted these to be garbage collected, you would need to explicitly set the object reference ( aClass ) to null so that the A instance, and it's contained delegate, were no longer reachable objects, and then you'd have to explicitly call GC.Collect to trigger the GC, since nothing will cause the GC to trigger in your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM