2011-05-19 79 views
1

我已经使用.net FileSystemWatcher类写了一个简单的测试工具。问题是我得到了内存泄漏,因为MemoryLeakTest的实例被FileSystemWatcher中更改的处理程序引用。清理这个引用的正确方法是什么,以便垃圾收集器可以在之后收集MemoryLeakTest实例和FileSystemWatcher实例?.net事件处理程序使用filesystemwatcher清理

要看到堆泄漏情况下,按照此说明: http://blogs.msdn.com/b/calvin_hsia/archive/2008/04/11/8381838.aspx

预先感谢您的咨询

using System; 
using System.IO; 

namespace MemoryLeakTest { 

class Leaking { 

    private FileSystemEventHandler changedHandler; 
    private FileSystemWatcher fsw; 

    public Leaking() { 
     changedHandler = new FileSystemEventHandler(fsw_Changed); 

     fsw = new FileSystemWatcher("c:\\", "*.*"); 
     fsw.Changed += changedHandler; 
     fsw.EnableRaisingEvents = true; 
    } 

    ~Leaking() { 
     fsw.Changed -= changedHandler; 
     fsw.Dispose();       
    } 

    void fsw_Changed(object sender, FileSystemEventArgs e) { 
     Console.WriteLine("Changed"); 
    } 

} 


class Program { 
    static void Main(string[] args) { 

     for (int i = 0; i < 100; ++i) { 
      var x = new Leaking(); 
     } 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     GC.Collect(); 

     Console.ReadLine(); 

    } 
} 
} 
+0

那么您应该实现IDisposable并在.Dispose()中执行清理,而不是在终结器中。 – asawyer 2011-05-19 12:57:24

+0

然后,我必须在某处手动调用Dispose()以获取较长的活动对象。我希望自动执行此步骤,所以我不会忘记这么做。 – agaga 2011-05-19 13:09:40

+1

经过一些研究后,似乎必须在每个具有实现IDisposable的成员的类中实现IDisposable。这似乎很容易将我的整个数据模型“污染”到根对象。我错过了什么吗? – agaga 2011-05-19 15:36:35

回答

1

是的,你应该实现在每一个类IDisposable有一名成员实现IDisposable。请参阅http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=VS.100).aspx

但是,实施IDisposable只是确保及时清理资源。通常,执行IDisposable失败不会导致长期内存泄漏,因为垃圾收集器最终会运行终结器,并且这些将释放操作系统资源。但不处置会导致短期内存泄漏和资源耗尽。它可以引起一个长期的问题,但是在使用BCL对象时这是相对不常见的。

例如,在上面的测试程序中,创建的Leaking对象在退出循环时是“无根的”,正如您看到垃圾收集器收集它们一样。如果你想让你的程序运行一段时间,做其他事情,这些对象将在下一次垃圾回收期间收集。这是一个短期的记忆和资源泄漏,但不是一个长期的问题。现在

,你可能碰上,如果你碰到这样的内存泄漏:

FileSystemWatcher watcher = new FileSystemWatcher(...); 
void MakeABigLeak() 
{ 
    for (int i = 0; i < 10; ++i) 
    { 
     var MyObject = new SomeObject(); 
     watcher.Changed += MyObject.ChangeHandler; 
    } 
} 

在这种情况下,watcher持有到每个创建的对象的引用。并且由于watcher已生根,这些对象将保持活动状态。他们不会被垃圾收集。

解决此问题的唯一方法是确保该对象从事件通知中删除自身。你可以在对象的Dispose方法中做到这一点,但也有其他方法。但是,您必须小心而不是在终结器中执行此操作。请记住,终结器没有设置顺序运行。有可能watcher是在引用它的对象之前完成的(因为两者都没有根,最终顺序无关紧要)。这就是为什么存在Dispose(bool)方法的原因:阻止您在定稿过程中访问其他对象。

这是您的选择。您可以编写一个Dispose方法,从事件通知中删除对象,或者您可以确保编写在对象不再对该事件感兴趣时进行删除的代码。在Dispose中这样做的缺点是执行订阅的对象必须保持对引发事件的对象的引用。

+0

谢谢你的回答。这是一个非常复杂的话题。目前我正在做一些关于事件处理程序的研究,弱引用... – agaga 2011-05-23 09:25:25