垃圾收集器是否清除订阅事件的对象?

5
如果我执行以下操作:
public class Test
{
    public static void Main()
    {
        List<Person> persons = new List<Person> { new Person() };

        persons[0].Sneezing += new EventHandler(Person_Sneezing);

        persons = null;
    }

    public static void Person_Sneezing(object sender, EventArgs e)
    {
        (sender as Person).CoverFace();
    }
}

如果 Sneezing 委托有对 Person_Sneezing 方法的引用,那么 person[0] 中的 person 对象是否仍然存在于内存中?或者它会被 GC 回收?

3个回答

8

垃圾回收器会收集该对象。要将对象保留在内存中,必须直接或间接地引用:

  1. 栈中的值
  2. 由强GC句柄引用的值
  3. 我暂时没想到的一些特殊情况

这不适用于persons [0]处的对象。所以它将被回收。

当然,这假设Person()的构造函数不会像将自己添加到ThreadLocalStorage那样做任何有趣的事情。


6
您已经完成了一半的工作;如果情况是反过来的,这将会造成内存泄漏。也就是说,如果代码如下所示:
public class Test
{
    public void HookupStuff()
    {
        List<Person> persons = new List<Person> { new Person() };

        this.EventHappened += new EventHandler(persons[0].SomeMethod);
        // persons[0].Sneezing += new EventHandler(Person_Sneezing);

        persons = null;
    }
}

现在,即使你将 persons 设置为 null,persons[0] 仍然存在,因为父类引用了其上的一个方法。

你会称之为泄漏吗?如果测试消失了,那么persons[0]也会消失。 - Matt Spradley
当然,但任何东西最终都会消失-最坏的情况是,在您的应用程序退出时所有内容都将消失!当您认为它会消失时它没有消失,我会称其为泄漏。 - mqp
(另外,如果您没有对persons [0]进行其他引用,那么您很难将其找回,因此它基本上泄漏到了虚无。) - mqp
对于一个如何造成内存泄漏的例子,试着在表单上显示的 ToolStrip 上没有首先将 Visible 设置为 false 的情况下销毁它。你会发现(如果你使用内存分析器)它仍然存在 - 因为当主题更改时,Windows 会触发一个事件通知所有带有主题的控件,而为了实现这一点,它会保持对 ToolStrip 的引用。(如果你稍微思考一下,你就会知道我为什么知道这个。提示:我不想知道它。) - Robert Rossney

1

除了你已经得到的答案,更现实的例子需要你小心处理。

如果你的应用程序有一个主窗口,它在程序运行期间一直存在,并且你经常创建“短暂”的对象,将它们的方法注册到主窗口上的事件中,那么当你不再需要这些对象时,你必须从这些事件中注销这些对象,否则这些对象将不会是“短暂”的 - 它们将像主窗口一样一直存在,即直到用户关闭应用程序。这将导致内存泄漏的效果。

如果你让短暂的对象类实现IDisposable,那么你可以在Dispose中注销事件,然后确保在想要丢弃对象时调用Dispose,这将有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接