簡體   English   中英

將對象設置為null vs Dispose()

[英]Setting an object to null vs Dispose()

我對CLR和GC的工作方式很着迷(我正在通過C#,Jon Skeet的書籍/帖子等閱讀CLR來擴展我的知識)。

無論如何,說:有什么區別:

MyClass myclass = new MyClass();
myclass = null;

或者,通過使MyClass實現IDisposable和析構函數並調用Dispose()?

此外,如果我有一個帶有using語句的代碼塊(例如下面的代碼),如果我單步執行代碼並退出using塊,那么對象是在處理垃圾收集時發生的嗎? 如果我在使用塊中調用Dispose()會發生什么?

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

流類(例如BinaryWriter)有一個Finalize方法嗎? 我為什么要用它?

將處理與垃圾收集分開是很重要的。 它們是完全不同的東西,有一個共同點我將在一分鍾內到達。

Dispose ,垃圾收集和定稿

當你編寫一個using語句時,它只是try / finally塊的語法糖,因此即使using語句正文中的代碼拋出異常,也會調用Dispose 並不意味着該對象在塊的末尾被垃圾收集。

處置是關於非托管資源 (非內存資源)。 這些可能是UI句柄,網絡連接,文件句柄等。這些是有限的資源,因此您通常希望盡快釋放它們。 只要您的類型“擁有”非托管資源,您應該直接(通常通過IntPtr )或間接(例如通過StreamSqlConnection等)實現IDisposable

垃圾收集本身只是關於內存 - 只有一點點扭曲。 垃圾收集器能夠找到無法再引用的對象,並釋放它們。 它不會一直尋找垃圾 - 只有當它檢測到它需要時(例如,如果堆的一個“代”耗盡內存)。

扭曲是最終確定 垃圾收集器保存一個不再可訪問的對象列表,但它們有一個終結器(在C#中寫成~Foo() ,有些令人困惑 - 它們與C ++析構函數完全不同)。 它在這些對象上運行終結器,以防它們需要在釋放內存之前進行額外的清理。

在該類型的用戶忘記以有序方式處理它的情況下,終結器幾乎總是用於清理資源。 因此,如果您打開FileStream但忘記調用DisposeClose ,則終結器最終將為您釋放基礎文件句柄。 在一個寫得很好的程序中,終結者幾乎不應該在我看來。

將變量設置為null

將變量設置為null一個小問題 - 為了垃圾收集,這幾乎不需要。 如果它是一個成員變量,你有時可能想要這樣做,盡管根據我的經驗,不再需要一個對象的“部分”。 當它是一個局部變量時,JIT通常足夠聰明(在發布模式下),以便知道何時不再使用引用。 例如:

StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();

// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);

// These aren't helping at all!
x = null;
sb = null;

// Assume that x and sb aren't used here

可能值得將局部變量設置為null是當你處於循環中時,循環的某些分支需要使用變量,但是你知道你已達到了一個你沒有的點。 例如:

SomeObject foo = new SomeObject();

for (int i=0; i < 100000; i++)
{
    if (i == 5)
    {
        foo.DoSomething();
        // We're not going to need it again, but the JIT
        // wouldn't spot that
        foo = null;
    }
    else
    {
        // Some other code 
    }
}

實現IDisposable / finalizers

那么,你自己的類型應該實現終結器嗎? 幾乎肯定不是。 如果你只是間接持有非托管資源(例如你有一個FileStream作為成員變量),那么添加你自己的終結器將無濟於事:當你的對象出現時,流幾乎肯定有資格進行垃圾收集,所以你可以依賴在FileStream有一個終結器(如果需要的話 - 它可能會引用別的東西等)。 如果你想“近乎”直接持有一個非托管資源, SafeHandle就是你的朋友 - 它需要一點時間才能開始,但這意味着你幾乎 不需要再次編寫終結器 如果你對資源有一個非常直接的處理( IntPtr ),你通常只需要一個終結器,你應該盡快轉移到SafeHandle (有兩個鏈接 - 理想情況下讀取兩個。)

Joe Duffy 有一套很長的關於終結者和IDisposable的指導方針 (與很多聰明的人合寫)值得一讀。 值得注意的是,如果你密封你的類,它會讓生活變得更容易:重寫Dispose以調用一個新的虛擬Dispose(bool)方法等的模式僅在你的類被設計用於繼承時才有意義。

這有點絮絮叨叨,但請你澄清一下你想要的地方:)

處置對象時,將釋放資源。 為變量賦值null時,您只需更改引用。

myclass = null;

執行此操作后,myclass所指的對象仍然存在,並將繼續執行直到GC開始清理它。 如果顯式調用Dispose,或者它在using塊中,則將盡快釋放所有資源。

這兩項行動彼此沒有太大關系。 當您將引用設置為null時,它只是這樣做。 它本身並不會影響所引用的類。 您的變量不再指向它以前使用的對象,但對象本身不變。

當你調用Dispose()時,它是對象本身的方法調用。 無論Dispose方法做什么,現在都在對象上完成。 但這不會影響您對該對象的引用。

唯一的重疊區域是, 不再有對象的引用時,它最終會被垃圾收集。 如果類實現了IDisposable接口,那么在對象被垃圾收集之前將調用Dispose()。

但是,在將引用設置為null之后,這不會立即發生,原因有兩個。 首先,可能存在其他引用,因此它根本不會收集垃圾,其次,即使這是最后一個引用,所以現在它已經准備好被垃圾收集,在垃圾收集器決定刪除之前不會發生任何事情物體。

在對象上調用Dispose()不會以任何方式“殺死”對象。 它通常用來清理,使該對象可以安全地刪除之后,但最終,沒有什么神奇的有關Dispose,它只是一個類的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM