列表对象的垃圾收集

时间:2012-12-21 05:36:30

标签: c# garbage-collection red-gate-ants

我想问一下清理和处理List(Of Object)中收集的对象的效率方法是什么?

通过调用List.Clear()方法会自动清除它收集的所有对象吗?

例如,考虑以下示例

public partial class Form1 : Form
{
    FontCollection m_fontCollection;

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        m_fontCollection = new FontCollection();
        for (int i = 0; i < 5000; i++)
        {
            Font font = new Font("Arial", 23);

            FontImpl impl = new FontImpl(font, Color.Black);
            impl.AfterChange += 
                new FontImpl.AfterChangeHandler(impl_AfterChange);

            m_fontCollection.Add(impl);
        }

        m_fontCollection.Dispose();
        MessageBox.Show("TakeSnap");
    }

    void impl_AfterChange()
    {
        throw new NotImplementedException();
    }
}

public class FontCollection : IEnumerable, IDisposable
{
    IList<FontImpl> m_Implementation = new List<FontImpl>();

    internal void Add(FontImpl impl)
    {
        this.m_Implementation.Add(impl);
    }

    public IEnumerator GetEnumerator()
    {
        return this.m_Implementation.GetEnumerator();
    }

    public void Dispose()
    {
        m_Implementation.Clear();
        m_Implementation = null;
    }
}

public class FontImpl
{
    private Font m_Font;
    private Color m_color;

    public FontImpl(Font newFont, Color newColcor)
    {
        m_Font = newFont;
        m_color = newColcor;
    }

    public event AfterChangeHandler AfterChange;

    public delegate void AfterChangeHandler();
}

当我运行上面的应用程序ANTS内存分析器时,我可以看到内存泄漏Font和FontFamily(我无法上传屏幕)以及如何删除这些泄漏

4 个答案:

答案 0 :(得分:2)

通常,您不必担心垃圾收集以及何时发生垃圾收集。如果对象没有引用,则该对象符合GC的条件。

完成IDisposable对象后,您确实需要在其上调用Dispose()。如果对象位于功能区的本地范围内,则using块可以轻松实现:

using (var resource = new SomeIDisposable()) {
    // use resource

    // resource.Dispose() is automatically called, *even if* an exception
    // is thrown.
}

可以导致内存泄漏,最后是对象的“悬空引用”,这会阻止它们被垃圾回收。最常见的原因是事件处理程序。当您订阅对象A公开的事件,并在对象B上使用事件处理程序时,A会获得对B的引用:

class B {
    void hook_up_to(A a) {
        a.SomeEvent += Handler;     // `a` gets a reference to `this`
    }

    void Handler(object sender, EventArgs e) {
    }
}

class Program {
    private A _a = new A();

    static void SomeMethod() {
        var b = new B();

        b.hook_up_to(_a);    // _a now has a reference to b

        // b goes out of scope.
    }
}

通常,当b超出范围时,它引用的对象将有资格进行垃圾回收。但在这种情况下,b与成员变量_a发布的事件相关联,导致_a获得对b的引用。现在有一个对b的未完成的引用,这是不可能 1 清除,b不适用于GC。这是内存泄漏。

1 在这种情况下引用b的唯一方法是this指针,如果/当事件处理程序被触发时。

答案 1 :(得分:1)

您不需要在课堂上实施IDisposable。通常,您的类需要实现IDisposable的唯一时间是它包含需要处理的其他类(数据库和网络连接以及非托管对象等)。

在您的示例中,您可能需要考虑在m_fontCollection中创建button1_Click 本地变量,因为您正在该方法中完全创建和处理它。如果你把它设为本地,那么一旦button1_Click退出就会被垃圾收集...... 提供没有剩余的引用(在这种情况下确实没有)。 / p>

答案 2 :(得分:0)

清除只删除列表中的所有内容。在您的情况下,您有一个IDisposable对象列表,您需要在列表中的所有项目上调用Dispose。呼叫清除不会那样做。

你的FontImpl应该实现IDisposable,因为它管理一个IDisposable对象:

 public void Dispose() {
      if (m_Font != null) {
          m_Font.Dispose();
          m_Font = null;
      }
 }

你的FontCollection Dispose应如下所示:

public void Dispose()
{
    foreach(FontImpl font in m_Implementation) {
        font.Dispose();
    }
    m_Implementation.Clear();
    m_Implementation = null;
}

答案 3 :(得分:0)

对于您显示的代码,您有Font个实现IDisposable的对象,因此应予以处置。这些对象由您的FontImpl课程管理,因此FontImpl应实施IDisposable。您的FontCollection类包含FontImpl个对象的列表,这些对象现在应该是一次性的,因此FontCollection需要实现IDisposable

你应该阅读IDisposable模式(这个SO答案提供了一些很好的信息 - https://stackoverflow.com/a/538238/416574)。

话虽如此,从你提供的代码片段来看,似乎没有从你的FontCollection获得任何东西而不是一个类来包装列表。除非你将要对该类进行其他操作,否则我将只有一个列表作为成员变量。这样,在表单的Dispose方法中,您可以遍历列表并处理FontImpl个对象。如果你想防止双重处理,那么在处理完所有内容后只需清空列表。