我有这段代码
public class Publisher
{
public event EventHandler SomeEvent;
}
public class Subscriber
{
public static int Count;
public Subscriber(Publisher publisher)
{
publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
}
~Subscriber()
{
Subscriber.Count++;
}
private void publisher_SomeEvent(object sender, EventArgs e)
{
// TODO
}
}
在我的应用程序的Main方法中,我有
static void Main(string[] args)
{
Publisher publisher = new Publisher();
for (int i = 0; i < 10; i++)
{
Subscriber subscriber = new Subscriber(publisher);
subscriber = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Subscriber.Count.ToString());
}
如果我运行这个程序,输出将会是0。 如果我从代码中移除事件订阅,我将得到期望的结果——也就是10。
当调用GC.Collect()时,垃圾回收被强制启动。因为Subscriber在其中定义了Finalize,所以GC将暂停收集,直到finalizequeue为空,也就是所有Subscription实例都将调用其Finalize()方法之后(如果我的假设不正确,请纠正我)。在下一行调用了GC.WaitForPendingFinalizers(),这将有效地挂起执行,直到终结器队列为空。现在,因为我们的输出为0,我相信Finalize()没有被调用,这使我相信GC没有标记订阅者实例要被收集,因此Finalizer()方法没有被调用。
所以我有两个问题:
1.我的假设是否正确,事件订阅是否阻止GC标记订阅者实例进行收集? 2.如果是这样,那是因为发布者持有对订阅者的引用吗?(垃圾收集器和事件处理程序)
我的猜想是,由于有 10 个 Subscriber 实例引用同一个 publisher 实例,当进行 GC 回收时,它会看到有其他对 publisher 的引用,因此无法回收,结果订阅实例和发布者一起被移动到下一代,因此垃圾回收不会发生,也不会在代码执行到 Console.WriteLine(Subscriber.Count.ToString()) 时调用 Finalize()。在这里我对吗?
Publisher.SomeEvent
委托时,它将执行当前订阅的任何内容。这意味着在订阅处于活动状态时,订阅者实例必须保持活动状态。这就是为什么发布者必须保留所有订阅者的引用。由于订阅者是可达的,GC无法收集它们。 - Andrew Savinykh