2011-05-19 110 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