[英]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
)或間接(例如通過Stream
, SqlConnection
等)實現IDisposable
。
垃圾收集本身只是關於內存 - 只有一點點扭曲。 垃圾收集器能夠找到無法再引用的對象,並釋放它們。 它不會一直尋找垃圾 - 只有當它檢測到它需要時(例如,如果堆的一個“代”耗盡內存)。
扭曲是最終確定 。 垃圾收集器保存一個不再可訪問的對象列表,但它們有一個終結器(在C#中寫成~Foo()
,有些令人困惑 - 它們與C ++析構函數完全不同)。 它在這些對象上運行終結器,以防它們需要在釋放內存之前進行額外的清理。
在該類型的用戶忘記以有序方式處理它的情況下,終結器幾乎總是用於清理資源。 因此,如果您打開FileStream
但忘記調用Dispose
或Close
,則終結器最終將為您釋放基礎文件句柄。 在一個寫得很好的程序中,終結者幾乎不應該在我看來。
將變量設置為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.