我想问一下清理和处理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(我无法上传屏幕)以及如何删除这些泄漏
答案 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
个对象。如果你想防止双重处理,那么在处理完所有内容后只需清空列表。