C# 操作和 GC

标签 c# delegates garbage-collection

我在 C# 中使用 Actions,我想知道一旦我希望 GC 正确收集对象,是否需要将 Action 的实例设置为 null?这是一个例子:

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

在我的 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");
 }
}

请阅读 Console.WriteLine 文本,这将帮助您理解我在这里的要求。

如果我将我对 GC 的理解应用到这个例子中,GC 将永远不会收集对象,因为 A 不能被销毁,因为它持有 B 的实例。我说得对吗?

如何正确收集这两个对象?我是否需要将 Actions 的实例设置为 null 只是为了让 GC 在应用程序结束之前收集对象,或者 GC 是否已经有某种非常智能的机制知道如何销毁具有 A 和 B 等 Actions 的对象?

编辑:问题是关于 GC 和正确收集对象的。它与调用方法 collect() 无关。

最佳答案

这个问题有很多问题。我不会直接回答您的问题,而是会回答您应该提出的问题。

让我们首先打消您对 GC 的看法。

Will sleeping for a long time activate the garbage collector?

没有。

What activates the garbage collector?

出于测试目的,您可以使用 GC.Collect()GC.WaitForPendingFinalizers()。仅将这些用于测试目的;除非在极少数情况下,否则在生产代码中使用它们是一种不好的做法。

在正常情况下,触发 GC 的事情很复杂; GC 是一台经过高度调整的机器。

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

转换为委托(delegate)的 lambda 的封闭外部变量的生命周期 延长 不短于委托(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?

在绝大多数情况下,不会。 垃圾收集器非常聪明;让它做它的工作,不要担心。最终,运行时将确定 Action 变量无法被任何事件根访问,并将使其符合收集条件;封闭的外部变量将成为合格的。

在极少数情况下,您可能希望尽快丢弃对 Action 的引用,但这种情况很少见;绝大多数时候,让 GC 在不受干扰的情况下完成它的工作。

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

是的。考虑:

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

当执行 M() 时,会为两个委托(delegate)创建一个闭包。假设 shortLived 即将被设置为 null,而 longLived 将在很远的将来设置为 null .不幸的是,两个 局部变量的生命周期都延长到了 longLived 引用的对象的生命周期,即使只有 c 仍然可以访问。在 longLived 中的引用失效之前,昂贵的资源 e 不会被释放。

许多编程语言都有这个问题;一些 JavaScript、Visual Basic 和 C# 的实现都有这个问题。在 C#/VB 的 Roslyn 版本中有人谈论修复它,但我不知道这是否会实现。

在那种情况下,解决办法是首先避免这种情况;如果其中一个委托(delegate)的生命周期比另一个长得多,请不要创建共享闭包的两个 lambda。

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

运行时可以证明不能再次读取本地的时刻,它引用的东西变得有资格收集(假设本地是唯一的根当然。)在您的示例程序中,不要求 aClassbClass 中的引用在方法结束之前保持有效。事实上,GC 可能会在一个线程上释放一个对象,而该对象仍在另一个线程的构造函数中,这种情况很少见但可能存在! GC 可以非常积极地确定什么是死的,所以要小心。

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

当然是 GC.KeepAlive()

关于C# 操作和 GC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16551018/

相关文章:

c# - 无法将方法组 '' 转换为非委托(delegate)类型 'System.Delegate'。您是否打算调用该方法?

f# - F# 中的阴影和垃圾收集

delphi - 德尔福的垃圾收集

c# - 在填充的矩形上绘制线条

c# - 检查列表中的间隔是否等长

c# - 如何将异步 Func 或 Action 转换为委托(delegate)并调用它

ios - 如何在等待 App Store 响应应用内购买时运行 'loading spinner' 或类似代码?

c - 如何实现平台无关的垃圾收集器?

c# - 验证服务描述符时出错 'ServiceType: INewsRepository Lifetime: Singleton ImplementationType: NewsRepository' :

c# - 查找行中任何单词的出现次数