2009-09-10 16 views
6

我前幾天在讀this文章,並想知道爲什麼有一個Finalizer和Dispose方法。我在SO上閱讀here以瞭解爲什麼您可能需要將Dispose添加到Finalizer。我的好奇心是,Finalizer什麼時候會被調用Dispose方法?有代碼示例還是基於軟件正在運行的系統上發生的事情?如果是這樣,那麼可能會發生沒有GC運行的Dispose方法。何時會處理方法不被調用?

回答

9

這裏的終結者的目的僅僅是防止內存泄漏的安全預防措施(如果您發生而不是明確呼叫Dispose)。這也意味着如果您希望他們在程序關閉時釋放資源,則無需處理對象,因爲無論如何GC都將被強制完成並收集所有對象。

作爲一個相關點,從終結者那裏處理對象略有不同是很重要的。

~MyClass() 
{ 
    Dispose(false); 
} 

public void Dispose() 
{ 
    Dispose(true); 
    GC.SuppressFinalize(this); 
} 

protected void Dispose(disposing) 
{ 
    if (!this.disposed) 
    { 
     if (disposing) 
     { 
      // Dispose managed resources here. 
     } 
     // Dispose unmanaged resources here. 
    } 
    this.disposed = true; 
} 

的原因,你希望在您的finaliser處置管理資源,你實際上這樣做可以創建他們強引用,這可能阻止GC做的工作正常和收集他們。當然,非託管資源(例如Win32句柄等)應該總是顯式關閉/處置,因爲CLR不知道它們。

+1

另一個不會在終結者中處理託管資源的原因......可能在您的終結者執行時他們可能已經GC'd了。嘗試在收集它們時收集它們會導致運行時錯誤。 – LukeH 2009-09-10 13:33:10

+1

@Luke:沒錯,但是可以通過將所有引用設置爲null,然後在處理之前執行空檢查來輕鬆避免。 – Noldorin 2009-09-10 13:49:33

+0

@Noldorin - 在你的例子中哪裏會註銷事件?我理解技術上他們會受到管理,但是如果我們通過某個事件綁定了一些與此類相關的對象,並且我們沒有在非託管部分取消註冊它(假設用戶不直接調用Dispose並且它的左邊直到GC來清理它向上)。將事件取消註冊到託管部分以確保發生這種情況是否安全?副作用可能是有人認爲他們正在處理一個對象,但實際上它從來不會因爲這個類和其他類之間的事件鏈接而被取消。 – SwDevMan81 2009-09-13 00:27:09

4

這主要是爲了保護自己。你無法指定你班級的最終用戶會做什麼。除了Dispose方法之外,GC還會「Dispose」你的對象,即使用戶忘記調用Dispose()或錯誤地使用你的類,也會適當地釋放你的資源。

+1

值得一提的是,GC是非確定性的,因此不能保證何時,甚至是否會調用您的finaliser。 – LukeH 2009-09-10 13:40:36

+0

是的 - 如果你的程序運行時間足夠長,你的對象最有可能被敲定。另外,如果它關閉乾淨,它會得到最終確定。但GC沒有保證 - 這是IDisposable首先存在的一部分。 – 2009-09-10 13:56:31

1

必須顯式調用dispose方法,方法是調用Dispose()或使用using語句中的對象。 GC將始終調用終結器,因此如果在對象處理完終結器之前有某些事情需要發生,至少應該檢查以確保對象中的所有內容都已清理完畢。

如果可能的話,您希望避免清理終結器中的對象,因爲與之前處理它們相比,它會導致額外的工作(比如調用dispose),但是至少應該檢查終結器中是否有對象躺在需要被刪除的周圍。

2

終止器在對象被垃圾收集時調用。 Dispose需要顯式調用。在下面的代碼中,終結器將被調用,但Dispose方法不會。

class Foo : IDisposable 
{ 
    public void Dispose() 
    { 
    Console.WriteLine("Disposed"); 
    } 

    ~Foo() 
    { 
    Console.WriteLine("Finalized"); 
    } 
} 

... 

public void Go() 
{ 
    Foo foo = new Foo(); 
} 
+1

這並非完全正確。在對象有資格進行垃圾收集之後(即應用程序不再引用實例),終結器會被調用一段時間。但是,由於必須爲實例運行終結器,因此CLR實際上是對象的根源,因此在終結器運行之前不會收集垃圾。 – 2009-09-10 11:48:38

+0

也不能保證一個對象將被GC'd或者它的終結器永遠被調用。這就是爲什麼確保你正確處理任何'IDisposable'對象這一點非常重要。 – LukeH 2009-09-10 13:27:18

0

尚未提及的一個重要但微妙的注意事項:Dispose很少考慮的目的是防止過早清理對象。帶終結器的對象必須仔細書寫,以免終止運行早於比預期。在最後一次方法調用開始之前,終結器無法運行,該方法將在對象(*)上進行,但如果對象在方法完成後將被放棄,則在最後一次方法調用期間,它有時可能會運行。正確處理對象的代碼在調用Dispose之前不能放棄對象,因此不會有終結器對正確使用Dispose的代碼造成嚴重破壞的危險。另一方面,如果使用對象的最後一個方法利用了在最後一次使用對象引用本身後在終結器中清理的實體,那麼垃圾收集器可以調用對象的Finalize並清理這些實體仍在使用中。補救措施是確保使用實體的任何調用方法必須在某個時刻通過使用「this」的方法調用來遵循。 GC.KeepAlive(this)是一個很好的方法。 (*)擴展爲不對對象執行任何操作的內聯代碼的非虛方法可免於此規則,但Dispose通常是或調用虛擬方法。