[英]C# Actions and GC
我在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之类的操作的对象的某种非常智能的机制?
编辑:问题是关于GC和正确收集对象。 它不是调用方法collect()。
这个问题有很多问题。 而不是直接回答你的问题,我将回答你应该问的问题。
让我们先解读你对GC的看法。
长时间睡觉会激活垃圾收集器吗?
没有。
是什么激活了垃圾收集器?
出于测试目的,您可以使用GC.Collect()
和GC.WaitForPendingFinalizers()
。 仅将它们用于测试目的; 除了在一些非常罕见的情况下,在生产代码中使用它们是一种不好的做法。
在正常情况下,触发GC的事情很复杂; GC是一种高度调整的机器。
就封闭的外部变量而言,垃圾收集的语义是什么?
被转换为代表的λ的封闭在外层变量的寿命 延长到不小于代表的寿命短 。
假设我有一个
Action
类型的变量,它使用lambda初始化,该lambda在引用类型的外部局部变量上关闭。 为了使该变量引用的对象可以收集,我是否必须将Action
类型的变量设置为null
?
在绝大多数情况下,没有。 垃圾收集器很聪明; 让它做它的工作,不要担心它 。 最终,运行时将确定任何实时根都无法访问Action
变量,并使其可以进行收集; 封闭的外部变量将符合条件。
在极少数情况下,您希望尽快丢弃对Action
引用,但它们很少见; 绝大多数时候,让GC不受干扰地完成工作。
是否存在外部变量的寿命延长太久的情况?
是。 考虑:
void M()
{
Expensive e = new Expensive();
Cheap c = new Cheap();
Q.longLived = ()=>c; // static field
Q.shortLived = ()=>e; // static field
}
执行M()
会为两个代理创建一个闭包。 假设shortLived
很快将被设置为null
,并且longLived
被设置为null
。 不幸的是, 两个局部变量的生命周期都延长到longLived
引用的对象的生命周期,即使只有c
仍然可以访问。 在longLived
的引用已经死亡之前,不会释放昂贵的资源e
。
许多编程语言都有这个问题; JavaScript,Visual Basic和C#的一些实现都存在这个问题。 有一些关于在C#/ VB的Roslyn版本中修复它的讨论,但我不知道这是否会实现。
在这种情况下,解决方案是首先避免这种情况; 如果其中一个代表的生活时间比另一个代表长得多,那么就不要让两个lambdas共享一个闭包。
在什么情况下, 不是封闭的外部变量的本地变得可以收集?
运行时可以证明本地无法再次读取的那一刻 ,它引用的东西变得可以收集(假设本地是当然唯一的根。)在你的示例程序中,没有要求aClass
中的引用和bClass
保持活着直到方法结束。 实际上,有一些罕见但可能的情况,GC可以在一个线程上解除分配一个对象, 而它仍然在另一个线程的构造函数中 ! GC可以非常积极地确定什么是死的,所以要小心。
面对激进的GC,我如何保持活力?
GC.KeepAlive()
当然。
我在C#中使用Actions,我想知道在希望GC正确收集对象后是否需要将Action的实例设置为null?
不必要。 只要没有引用该委托的对象,该委托就有资格获得GC。
话虽如此,在您的示例中, aClass
和bClass
仍然是有效变量,并引用可到达的对象。 这意味着aClass.a
仍然可以访问,并且不符合GC的条件,因此不会收集它。
如果您希望将这些文件进行垃圾回收,则需要将对象引用( aClass
)显式设置为null,以便A
实例及其包含的委托不再是可访问的对象,然后您必须显式调用GC.Collect
以触发GC,因为没有任何东西会导致GC在您的代码中触发。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.