简体   繁体   English

C#动作和GC

[英]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? 我在C#中使用Actions,我想知道在希望GC正确收集对象后是否需要将Action的实例设置为null? 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: 在我的Main方法中我有这样的东西:

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. 请阅读Console.WriteLine文本,它将帮助您理解我在这里问的问题。

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? 如果我将GC的理解应用于此示例,则GC将永远不会收集对象,因为A不能被销毁,因为它包含B的实例。我是对的吗?

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? 我是否需要将Actions的实例设置为null以便让GC在应用程序结束之前收集对象,或者GC是否已经知道如何销毁具有诸如A和B之类的操作的对象的某种非常智能的机制?

EDIT: The question is about GC and collecting objects properly. 编辑:问题是关于GC和正确收集对象。 Its not about calling the method collect(). 它不是调用方法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. 让我们先解读你对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() . 出于测试目的,您可以使用GC.Collect()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; 在正常情况下,触发GC的事情很复杂; the GC is a highly tuned piece of machinery. GC是一种高度调整的机器。

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. 假设我有一个Action类型的变量,它使用lambda初始化,该lambda在引用类型的外部局部变量上关闭。 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 ? 为了使该变量引用的对象可以收集,我是否必须将Action类型的变量设置为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; 最终,运行时将确定任何实时根都无法访问Action变量,并使其可以进行收集; 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; 在极少数情况下,您希望尽快丢弃对Action引用,但它们很少见; the vast majority of time, just let the GC do its job without interference. 绝大多数时候,让GC不受干扰地完成工作。

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. 执行M()会为两个代理创建一个闭包。 Suppose that shortLived is going to be set to null soon, and longLived is set to null far in the future. 假设shortLived很快将被设置为null ,并且longLived被设置为null 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. 不幸的是, 两个局部变量的生命周期都延长到longLived引用的对象的生命周期,即使只有c仍然可以访问。 The expensive resource e is not released until the reference in longLived is dead. longLived的引用已经死亡之前,不会释放昂贵的资源e

Numerous programming languages have this problem; 许多编程语言都有这个问题; some implementations of JavaScript, Visual Basic, and C# all have this problem. JavaScript,Visual Basic和C#的一些实现都存在这个问题。 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. 有一些关于在C#/ VB的Roslyn版本中修复它的讨论,但我不知道这是否会实现。

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. 如果其中一个代表的生活时间比另一个代表长得多,那么就不要让两个lambdas共享一个闭包。

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. 运行时可以证明本地无法再次读取的那一刻 ,它引用的东西变得可以收集(假设本地是当然唯一的根。)在你的示例程序中,没有要求aClass中的引用和bClass保持活着直到方法结束。 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 ! 实际上,有一些罕见但可能的情况,GC可以在一个线程上解除分配一个对象, 而它仍然在另一个线程的构造函数中 The GC can be very aggressive about determining what is dead, so be careful. GC可以非常积极地确定什么是死的,所以要小心。

How do I keep something alive in the face of an aggressive GC? 面对激进的GC,我如何保持活力?

GC.KeepAlive() of course. GC.KeepAlive()当然。

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? 我在C#中使用Actions,我想知道在希望GC正确收集对象后是否需要将Action的实例设置为null?

Not necessarily. 不必要。 As long as no object is reachable which references the delegate, the delegate will be eligible for GC. 只要没有引用该委托的对象,该委托就有资格获得GC。

That being said, in your example, aClass and bClass are still valid variables, and refer to reachable objects. 话虽如此,在您的示例中, aClassbClass仍然是有效变量,并引用可到达的对象。 This means that aClass.a is still reachable, and not eligible for GC, so it will not be collected. 这意味着aClass.a仍然可以访问,并且不符合GC的条件,因此不会收集它。

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. 如果您希望将这些文件进行垃圾回收,则需要将对象引用( aClass )显式设置为null,以便A实例及其包含的委托不再是可访问的对象,然后您必须显式调用GC.Collect以触​​发GC,因为没有任何东西会导致GC在您的代码中触发。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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