程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 編寫高質量代碼改善C#程序的157個建議——建議52:及時釋放資源,

編寫高質量代碼改善C#程序的157個建議——建議52:及時釋放資源,

編輯:C#入門知識

編寫高質量代碼改善C#程序的157個建議——建議52:及時釋放資源,


建議52:及時釋放資源

垃圾回收機制自動為我們隱式地回收了資源(垃圾回收器會自動調用終結器),那我們為什麼要主動釋放資源呢?

private void buttonOpen_Click(object sender,EventArgs e)
{
   FileStream fileStream = new FileStream(@"c:\test.txt",FileMode.Open);           
}

private void buttonGC_Click(object sender,EventArgs e)
{
   System.GC.Collect();   
}

這是一個WinForm窗體程序的例子,在這個示例中,單擊一個按鈕負責打開一個文件,單擊另一個按鈕負責回收說有“代”(代的概念會在下文詳細指出)的垃圾。如果連續兩次單擊打開文件的按鈕,系統就會報錯:

IOException:文件“c:\test.txt”正由另外一進程使用,因此該進程無法訪問此文件。

如果先單擊打開文件的按鈕,繼而再單擊清理按鈕,則運行正常。

 

現在來分析:在打開文件的方法中,方法執行完畢後,由於局部變量fileStream在程序中已經沒有任何地方引用了,所以它會在下一次垃圾回收時被運行時標記為垃圾。那麼,什麼時候會進行下一次垃圾回收呢,後者說垃圾回收器什麼時候才開始真正進行回收工作呢?微軟官方的解釋是,當滿足以下條件之一時將發生垃圾回收:

  • 系統具有低的物理內存。
  • 由托管堆上已分配的對象使用的內存超出了可接受的阈值。 這意味著可接受的內存使用的阈值已超過托管堆。 隨著進程的運行,此阈值會不斷地進行調整。
  • 調用 GC.Collect 方法。 幾乎在所有情況下,您都不必調用此方法,因為垃圾回收器會持續運行。 此方法主要用於特殊情況和測試。

 

不及時釋放資源是對系統的一種極大的浪費,這種浪費還會干擾程序的正常運行(如在本實例中,由於它始終占著文件資源,導致我們不能再次使用這個文件資源了)。

若果類型本身繼承了IDisposable接口,垃圾回收機制雖然會自動幫我們釋放資源,但是這個過程卻延長了,因為它不是在一次回收中完成所有的清理工作。本例中因為fileStream繼承了IDisposable接口,故第一次進行垃圾回收時,垃圾回收器會調用fileStream的終結器,然後等待下一次的垃圾回收,這時fileStream對象才有可能被真正的回收掉。

我們來改進這個程序:

private void buttonOpen_Click(object sender,EventArgs e)
{
    FileStream fileStream = new FileStream(@"c:\test.txt",FileMode.Open);     
    fileStream.Dispose();      
}

但是如果第一行代碼出現異常,就永遠執行不了filsStream.Dispose()了。再次改進:

private void buttonOpen_Click(object sender,EventArgs e)
{
    try
    {   
        FileStream fileStream = new FileStream(@"c:\test.txt",FileMode.Open);     
    }
    finally
    {
        fileStream.Dispose();      
    }
}

使用語法糖“using"關鍵字進一步簡化:

private void buttonOpen_Click(object sender,EventArgs e)
{
    using(FileStream fileStream = new FileStream(@"c:\test.txt",FileMode.Open))
    {
    
    }
}

 

 

關於“代數”:一共分為3代:第0代、第1代、第2代。

  • 第 0 代:這是最年輕的代,其中包含短生存期對象。 短生存期對象的一個示例是臨時變量。 垃圾回收最常發生在此代中。新分配的對象構成新一代的對象並且為隱式的第 0 代回收,除非它們是大對象,在這種情況下,它們將進入第 2 代回收中的大對象堆。大多數對象通過第 0 代中的垃圾回收進行回收,不會保留到下一代。
  • 第 1 代:這一代包含短生存期對象並用作短生存期對象和長生存期對象之間的緩沖區。
  • 第 2 代:這一代包含長生存期對象。 長生存期對象的一個示例是服務器應用程序中的一個包含在進程期間處於活動狀態的靜態數據的對象。

當條件得到滿足時,垃圾回收將在特定代上發生。 回收某個代意味著回收此代中的對象及其所有更年輕的代。 第 2 代垃圾回收也稱為完整垃圾回收,因為它回收所有代上的所有對象(即,托管堆中的所有對象)。

幸存和提升

垃圾回收中未回收的對象也稱為幸存者,並會被提升到下一代。 在第 0 代垃圾回收中幸存的對象將被提升到第 1 代;在第 1 代垃圾回收中幸存的對象將被提升到第 2 代;而在第 2 代垃圾回收中幸存的對象將仍為第 2 代。

當垃圾回收器檢測到某個代中的幸存率很高時,它會增加該代的分配阈值,因此下一次回收將會獲取一個非常大的回收內存。 CLR 會在以下兩個優先級別之前進行平衡:不允許應用程序的工作集獲取太大內存以及不允許垃圾回收花費太多時間。

暫時代和暫時段

因為第 0 代和第 1 代中的對象的生存期較短,因此,這些代被稱為暫時代。

暫時代必須在稱為暫時段的內存段中進行分配。 垃圾回收器獲取的每個新段將成為新的暫時段,並包含在第 0 代垃圾回收中幸存的對象。 舊的暫時段將成為新的第 2 代段。

 

更多內容可參考MSDN:垃圾回收的基礎

 

 

轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved