4

我在谷歌上搜索没有得到我想要的。我不知道我是对还是错。看,我试图理解 GC.Collect() 所以这里是代码..

public class SomePublisher
{
    public event EventHandler SomeEvent;
}

public class SomeSubscriber
{
    public static int Count;

    public SomeSubscriber(SomePublisher publisher)
    {
        publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
    }

    ~SomeSubscriber()
    {
        SomeSubscriber.Count++;
    }

    private void publisher_SomeEvent(object sender, EventArgs e)
    {
        // TODO: something
    }
}

我在我的主线程中这样做..

 SomePublisher publisher = new SomePublisher();

        for (int i = 0; i < 10; i++)
        {
            SomeSubscriber subscriber = new SomeSubscriber(publisher);
            subscriber = null;
        }

        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine(SomeSubscriber.Count.ToString());
        Console.ReadLine();

我得到输出 0,但根据我的说法应该是 10,因为 GC.Collect() 必须从内存中删除 class1 对象,因此必须调用 class1 析构函数,因此计数必须增加到 10 ..任何人都可以解释这一点..

4

2 回答 2

8

(还要注意,C# 没有析构函数1,它有终结器。这些是非常不同的东西,你不应该混淆它们。)

“问题”在这一行:

publisher.SomeEvent += new EventHandler(publisher_SomeEvent);

这将创建一个针对publisher_SomeEvent()特定对象实例的方法的委托,并将此委托添加到publisher.SomeEvent事件的调用列表中。这个委托对象引用了目标对象,它阻止了对象被收集!(这是一件好事——如果您将委托带到特定对象上的方法,那么您不希望在不再引用委托之前收集该对象。)

这在技术上根本不是问题,而是运行时使仍然被引用的对象保持活动状态。

为了说明,这是引用链:

SomePublisher -+-> EventHandler --> SomeSubscriber
               |
               +-> EventHandler --> SomeSubscriber
               |
               +-> (Eight more...)

在调用之前,您需要做以下两件事之一GC.Collect()

  1. 在释放每个SomePublisher对象之前取消订阅事件。这将使EventHandler委托实例和SomeSubscriber它们引用的实例都符合收集条件。
  2. 设置publisher = null;。这将导致整个对象图变得符合收集条件。

SomeSubscriber在这两种情况下,这都会释放对对象的所有引用。


1请注意,C# 规范确实将这些代码块称为“析构函数”,但这是一个可怕的名称。那些熟悉垃圾收集语言的人会对此感到困惑,因为“终结器”是当对象不再可访问时由垃圾收集器调用的代码的通用术语。C++ 开发人员尤其希望析构函数在不同的时间执行。所以是的,C# 有一个叫做“析构函数”的东西,但它不是析构函数。(说些什么并不会让它如此!)

于 2013-08-13T07:20:59.690 回答
1

SomeSubcriber 对象的终结器直到结束才会被调用。因为即使 SomeSubscriber 对象被设置为 null,它的内存仍然被 SomePublisher 对象的事件 SomeEvent 引用,该事件一直存活到应用程序结束。因此,当调用 GC.Collect() 时,垃圾收集器将找不到任何对象放入终结器队列。

public SomeSubscriber(SomePublisher publisher)
{
// publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
}

如果我们可以用上面的代码替换 SomeSubcriber 的构造函数,那么我们会在控制台中得到 10 的结果。

于 2013-10-10T09:32:26.153 回答