我在 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 typeAction
tonull
?
在绝大多数情况下,不会。 垃圾收集器非常聪明;让它做它的工作,不要担心。最终,运行时将确定 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?
运行时可以证明不能再次读取本地的时刻,它引用的东西变得有资格收集(假设本地是唯一的根当然。)在您的示例程序中,不要求 aClass
和 bClass
中的引用在方法结束之前保持有效。事实上,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/