[英]Where did variable = null as “object destroying” come from?
我在許多不同公司的不同版本的.NET上使用許多舊系統編寫的遺留系統,在不斷尋找以下模式的示例:
public void FooBar()
{
object foo = null;
object bar = null;
try
{
foo = new object();
bar = new object();
// Code which throws exception.
}
finally
{
// Destroying objects
foo = null;
bar = null;
}
}
對於任何知道.NET中內存管理工作原理的人來說,這種代碼都是不必要的。 垃圾收集器不需要您手動分配null
來告訴您可以收集舊對象,也不需要分配null
指示GC立即收集對象。
這種模式只是噪音,使理解代碼試圖達到的目標變得更加困難。
那么,為什么我一直在尋找這種模式呢? 有學校教這種做法嗎? 是否有一種語言需要將null
值分配給本地范圍的變量才能正確管理內存? 在顯式分配我還沒有理解過的null
,是否還有其他附加值?
開發人員使用的是FUD 貨物崇拜程序 (感謝Daniel Earwicker ),他們習慣於“釋放”資源,不良的GC實現和不良的API。
一些GC不能很好地處理循環引用。 要擺脫它們,您必須在“某處”中斷循環。 哪里? 好吧,如果有疑問,那么到處都是。 這樣做一年,它已經觸手可及。
同時將該字段設置為null
會給您“做某事”的想法,因為作為開發人員,我們總是害怕“忘記某事”。
最后,我們有必須顯式關閉的API,因為沒有真正的語言支持說“完成后關閉它”,讓計算機像使用GC一樣弄清楚它。 因此,您有一個必須在其中調用清除代碼的API,而沒有一個您需要在其中調用的API。 這很爛,並鼓勵了像上面這樣的模式。
可能來自VB,它使用了引用計數策略進行內存管理和對象生存期。 將引用設置為Nothing
(等同於null)將減少引用計數。 一旦該計數變為零,則該對象將被同步銷毀。 離開方法范圍后,計數將自動減少,因此即使在VB中,該技術也幾乎沒有用,但是在某些特殊情況下,您可能會貪婪地破壞對象,如以下代碼所示。
Public Sub Main()
Dim big As Variant
Set big = GetReallyBigObject()
Call big.DoSomething
Set big = Nothing
Call TimeConsumingOperation
Call ConsumeMoreMemory
End Sub
在上面的代碼中,由big
引用的對象將一直持續到最后,而無需調用Set big = Nothing
。 如果方法中的其他內容是耗時的操作或產生了更多的內存壓力,則這可能是不希望的。
它來自C / C ++,其中明確要求將指針設置為null是正常的做法(以消除懸空的指針 )
調用free()之后:
#include <stdlib.h>
{
char *dp = malloc ( A_CONST );
// Now that we're freeing dp, it is a dangling pointer because it's pointing
// to freed memory
free ( dp );
// Set dp to NULL so it is no longer dangling
dp = NULL;
}
經典的VB開發人員在編寫其COM組件以防止內存泄漏時也做了同樣的事情。
在具有確定性垃圾回收功能且沒有RAII的語言(例如舊的Visual Basic)中,它更為常見,
但是即使在那兒也沒有必要,
並且經常有必要打破循環引用。 因此,這可能實際上是源於糟糕的C ++程序員,他們在各處都使用啞指針。 在C ++中,有必要在刪除啞指針后將其設置為0,以防止重復刪除。
我已經在VBScript代碼(經典ASP)中看到了很多,我認為它來自那里。
我認為這曾經是前C / C ++開發人員中的常見誤解。 他們知道GC將釋放他們的內存,但是他們並不真正了解何時何地。 只需清潔並繼續:)
我懷疑這種模式來自將C ++代碼轉換為C#而沒有暫停以了解C#終結與C ++終結之間的區別。 在C ++中,出於調試目的(以便您可以在調試器中看到該引用不再有效),或者出於我希望釋放智能對象的原因,我經常將析構函數中的內容清空。 (如果那是我的意思,我寧願在其上調用Release ,並使維護人員使代碼的含義清晰明了。)正如您所注意到的那樣,在C#中這幾乎是毫無意義的。
由於不同的原因,您也始終在VB / VBScript中看到此模式。 我想了一下這可能導致的原因:
http://blogs.msdn.com/b/ericlippert/archive/2004/04/28/122259.aspx
它來自C / C ++,其中對已發布的指針執行free()/ delete可能會導致崩潰,而釋放NULL指針則無濟於事。
這意味着此構造(C ++)將導致問題
void foo()
{
myclass *mc = new myclass(); // lets assume you really need new here
if (foo == bar)
{
delete mc;
}
delete mc;
}
雖然這將工作
void foo()
{
myclass *mc = new myclass(); // lets assume you really need new here
if (foo == bar)
{
delete mc;
mc = NULL;
}
delete mc;
}
結論:在C#,Java和幾乎任何其他垃圾收集語言中,IT都是完全不必要的。
分配空值的約定可能是由於foo
是實例變量而不是局部變量這一事實而引起的,您應該在GC可以收集它之前刪除該引用。 在第一句話中有人睡覺了,並開始使所有變量無效。 人群隨之而來。
考慮稍作修改:
public void FooBar()
{
object foo = null;
object bar = null;
try
{
foo = new object();
bar = new object();
// Code which throws exception.
}
finally
{
// Destroying objects
foo = null;
bar = null;
}
vavoom(foo,bar);
}
作者可能想要確保如果先前引發並捕獲了異常,則大型Vavoom(*)不會獲得指向格式錯誤的對象的指針。 偏執狂導致防御性編碼,這在這行業中不一定是一件壞事。
(*)如果您知道他是誰,就知道。
VB開發人員必須處置所有對象,以嘗試減少內存泄漏的機會。 我可以想象這是VB開發人員遷移到.NEt / c#時的原因
我可以看到它是由於對垃圾收集的工作方式有誤解,或者是迫使GC立即啟動的努力-也許是因為對象foo
和bar
非常大。
我以前在一些Java代碼中已經看到了。 它用在靜態變量上,以表示應該銷毀該對象。
不過,它可能不是起源於Java,因為將其用於除靜態變量以外的任何事情在Java中也沒有意義。
它來自C ++代碼,尤其是智能指針 。 在這種情況下,它相當於C#中的.Dispose()
。
這不是一個好習慣,最多是開發人員的本能。 通過在C#中分配null
並沒有實際值,除了可能有助於GC中斷循環引用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.