2

我想问一下清理和处理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

4 回答 4

3

通常,您不必担心垃圾收集以及何时发生。当一个对象的引用为零时,它就有资格进行 GC。

但是,您应该注意的是IDisposable对象。当你完成一个IDisposable对象时,你真的需要调用Dispose()它。如果对象位于函数的本地范围内,那么using块可以轻松完成:

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

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

可能会通过以对象的“悬空引用”结束来导致内存泄漏,这会阻止它们被垃圾收集。最常见的原因是事件处理程序。当您订阅由 object 公开的事件时A,使用 object 上的事件处理程序BA获取对以下内容的引用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 的条件。这是内存泄漏。

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

于 2012-12-21T05:39:32.953 回答
1

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

在您的示例中,您可能需要考虑在中创建m_fontCollection一个局部变量button1_Click,因为您完全在该方法中创建和处理它。如果您将其设为本地,那么一旦button1_Click退出它将被垃圾收集......前提是没有对它的剩余引用(在这种情况下确实没有)。

于 2012-12-21T05:43:55.917 回答
0

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

你的 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;
}
于 2012-12-21T06:00:14.450 回答
0

在您展示的代码的情况下,您有Font实现的对象,IDisposable因此应该被处置。这些对象由您的FontImpl类管理,因此FontImpl应该实现IDisposable. 您的FontCollection类包含一个对象列表,FontImpl这些对象现在应该是一次性的,因此FontCollection需要实现IDisposable

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

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

于 2012-12-21T06:00:19.820 回答