.net事件处理程序使用filesystemwatcher清理

时间:2011-05-19 12:43:37

标签: .net garbage-collection filesystemwatcher

我使用.net FileSystemWatcher类编写了一个简单的测试工具。问题是我得到了内存泄漏,因为MemoryLeakTest的实例被FileSystemWatcher中更改的处理程序引用。清理此引用的正确方法是什么,以便垃圾收集器可以在之后收集MemoryLeakTest实例和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();

    }
}
}

2 个答案:

答案 0 :(得分:2)

您可能还遇到了FileWatcher本身的内存泄漏问题。请参阅以下内容:http://connect.microsoft.com/VisualStudio/feedback/details/565720/memory-leak-in-system-io-filesystemwatcher

答案 1 :(得分: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中执行此操作的缺点是执行订阅的对象必须维护对引发事件的对象的引用。