簡體   English   中英

變量= null作為“銷毀對象”從何而來?

[英]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立即啟動的努力-也許是因為對象foobar非常大。

我以前在一些Java代碼中已經看到了。 它用在靜態變量上,以表示應該銷毀該對象。

不過,它可能不是起源於Java,因為將其用於除靜態變量以外的任何事情在Java中也沒有意義。

它來自C ++代碼,尤其是智能指針 在這種情況下,它相當於C#中的.Dispose()

這不是一個好習慣,最多是開發人員的本能。 通過在C#中分配null並沒有實際值,除了可能有助於GC中斷循環引用。

暫無
暫無

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

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