带有活动事件处理程序的未引用对象的垃圾收集

在以下程序中……

using System; class Program { static Parent parent; static void Main( string[] args ) { parent = new Parent(); // The program hereafter runs for a long time and occasionally // causes parent.SomeEvent to be raised. } } class Parent { public event EventHandler SomeEvent; public Parent() { new Handler( this ); } } class Handler { public Handler( Parent parent ) { parent.SomeEvent += parent_SomeEvent; } void parent_SomeEvent( object sender, EventArgs e ) { // Does something important here. } } 

请注意,虽然已订阅SomeEvent ,但未引用实例化的Handler对象。 是否有可能在程序运行一段时间后,垃圾收集器可能决定取消Handler实例,因此只要parent.SomeEvent就不再调用其parent_SomeEvent处理程序?

我需要对我正在撰写的应用程序进行澄清。 有许多类似Handler的对象,如上所示进行实例化,没有被引用。 Handler主要目的是订阅SomeEvent 。 没有有用的方法来调用对Handler实例的引用,所以我可以不参考它。 调试时我没有遇到任何问题。 但现在我担心在应用程序长时间运行并且垃圾收集器更活跃时,部署后可能会出现问题。

当您运行程序时, Parent类保存对您在Handler构造函数中创建的EventHandler委托实例的引用,并且委托实例保存对Handler实例的引用。 因此,只要存在对Parent实例的引用, Handler实例就不会被垃圾回收。

在删除对该对象的所有引用之前,不会对Handler类的实例化对象进行垃圾回收。

因此,在您取消订阅所有事件处理程序之前,该对象将存在。 因为订阅的事件处理程序也是将Parent实例连接到Handler实例的另一个“引用”。

是否有可能在程序运行一段时间后,垃圾收集器可能决定取消Handler实例,因此只要引发parent.SomeEvent,就不再调用其parent_SomeEvent处理程序?

这正是GC只从堆中收集“未引用”对象的原因。 您的场景将导致未定义的NullReferenceException ,完全取决于GC何时决定删除对象。 幸运的是,事实并非如此:)。

此外,GC足够智能,可以确定未引用对象(未引用的岛)的孤立池。 因此,在场景中,您的父对象也会被取消引用,而GC将确定整个对象链是未引用的( Parent对象, event订阅和handler对象),并将在下一个收集周期中将它们全部收集在一起。

如果可以,我会推荐这篇MSDN文章。 为您提供.NET中垃圾收集的广泛概念的概述。 在编码时要记住非常有用。

程序一直存在,并且它引用了Parent的实例,因此Parent永远不会被垃圾收集(直到程序结束)。

Parent通过它的事件处理程序SomeEvent保留委托集合,因此其中的任何委托都不会被垃圾回收。

所以简而言之,不,它不会被垃圾收集。