1. 程式人生 > >C#或者.NET下的強制垃圾回收辦法

C#或者.NET下的強制垃圾回收辦法

今天來談談C#的GC,也就是垃圾回收機制,非常的受教,總結如下
首先:談談託管,什麼叫託管,我的理解就是託付C#執行環境幫我們去管理,在這個執行環境中可以幫助我們開闢記憶體和釋放記憶體,開闢記憶體一般用new,記憶體是隨機分配的,釋放主要靠的是GC也就是垃圾回收機制。哪麼有兩個大問題 1.GC可以回收任何物件嗎?2.GC什麼時候來回收物件?回收那些物件?
對於第一個問題,GC可以回收任何物件嗎?我是這樣理解的,首先要明白一點,C#在強大也管不到非託管程式碼?哪麼什麼是非託管程式碼呢?比如stream(檔案),connection(資料庫連線),COM(元件)等等。。哪麼這些物件是需要進行連線的,比如說我們寫這樣一句話FileStream fs = new FileStream(“d://a.txt”,FileMode.Open);實際上已經建立了和d://a.txt的連線,如果重複兩次就會報錯。哪麼fs這個物件叫做非託管物件,也就是說C#

不能自動去釋放和d://a.txt的連線。哪麼對於非託管的程式碼怎麼辦,一會我來說。
    對於第二個問題,GC什麼時候來回收,回收什麼物件?我想後面的就不用我說了,當然是回收託管物件了。但是GC什麼時候回收?是這樣的:GC是隨機的,沒有人知道他什麼時候來,哪麼我寫了一個例子,證明這一點
private void button1_Click(object sender, EventArgs e)

{           

AA a = new AA();

AA b = new AA();

AA c = new AA();

AA d = new AA();

}

public class AA{}

在講這個例子之前,要明白什麼被稱之為垃圾,垃圾就是一個記憶體區域,沒有被任何引用指向,或者不再會被用到。哪麼在第一次點選按鈕的時候會生成4個物件,第二次點選按鈕的時候也會生成4個物件,但是第一次生成的4個物件就已經是垃圾了,因為,第一次生成的4個物件隨著button1_Click函式的結束而不會再被呼叫(或者說不能再被呼叫),哪麼這個時候GC就會來回收嗎?不是的!我說了GC是隨機的,哪麼你只管點你的,不一會GC就會來回收的(這裡我們可以認為,記憶體中存在一定數量的垃圾之後,GC會來),要證明GC來過我們把AA類改成
public class AA

{

~AA()

{

        MessageBox.Show("解構函式被執行了");

}

}

要明白,GC清理垃圾,實際上是呼叫解構函式,但是這些程式碼是託管程式碼(因為裡面沒有涉及到Steam,Connection等。。)所以在解構函式中,我們可以只寫一個MsgBox來證明剛的想法;這個時候,執行你的程式,一直點選按鈕,不一會就會出現一大堆的“解構函式被執行了”…

    好了,然後讓我們看看能不能改變GC這種為所欲為的天性,答案是可以的,我們可以通過呼叫GC.Collect();來強制GC進行垃圾回收,哪麼button1_Click修改如下
private void button1_Click(object sender, EventArgs e)

{           

AA a = new AA();

AA b = new AA();

AA c = new AA();

AA d = new AA();

GC.Collect();

}

哪麼在點選第一次按鈕的時候,生成四個物件,然後強制垃圾回收,這個時候,會回收嗎?當然不會,因為,這四個物件還在執行中(方法還沒結束),當點第二次按鈕的時候,會出現四次"解構函式被執行了",這是在釋放第一次點選按鈕的四個物件,然後以後每次點選都會出現四次"解構函式被執行了",哪麼最後一次的物件什麼時候釋放的,在關閉程式的時候釋放(因為關閉程式要釋放所有的記憶體)。
 

好了,現在來談談非託管程式碼,剛才說過,非託管程式碼不能由垃圾回收釋放,我們把AA類改成如下
public class AA

{

     FileStream fs = new FileStream("D://a.txt",FileMode.Open);

     ~AA()

     {

            MessageBox.Show("解構函式被執行了");

 }

}

private void button1_Click(object sender, EventArgs e)

{

            AA a = new AA();

}

如果是這樣一種情況,哪麼第二次點選的時候就會報錯,原因是一個檔案只能建立一個連線。哪麼一定要釋放掉第一個資源,才可以進行第二次的連線。哪麼首先我們想到用GC.Collect(),來強制釋放閒置的資源,修改程式碼如下:
private void button1_Click(object sender, EventArgs e)

{

            GC.Collect();

            AA a = new AA();

}

哪麼可以看到,第二次點按鈕的時候,確實出現了“解構函式被執行了“,但是程式仍然錯了,原因前面我說過,因為Stream不是託管程式碼,所以C#不能幫我們回收,哪怎麼辦?
自己寫一個Dispose方法;去釋放我們的記憶體。程式碼如下:
public class AA:IDisposable

    {

        FileStream fs = new FileStream("D://a.txt",FileMode.Open);

        ~AA()

        {

            MessageBox.Show("解構函式被執行了");

        }

        #region IDisposable 成員
 

        public void Dispose()

        {

            fs.Dispose();

            MessageBox.Show("dispose執行了");

        }

        #endregion

    }

好了,我們看到了,繼承IDisposable介面以後會有一個Dispose方法(當然了,你不想繼承也可以,但是介面給我們提供一種規則,你不願意遵守這個規則,就永遠無法融入整個團隊,你的程式碼只有你一個人能看懂),好了閒話不說,這樣一來我們的button1_Click改為private void button1_Click(object sender, EventArgs e)

{

            AA a = new AA();

a.Dispose();

}

我們每次點選之後,都會發現執行了“dispose執行了”,在關閉程式的時候仍然執行了“解構函式被執行了”這意味了,GC還是工作了,哪麼如果程式改為:
private void button1_Click(object sender, EventArgs e)

{

            AA a = new AA();

a.Dispose();

GC.Collect();

}

每次都既有“dispose執行了又有”“解構函式被執行了”,這意味著GC又來搗亂了,哪麼像這樣包含Stream connection的物件,就不用GC來清理了,只需要我們加上最後一句話GC.SuppressFinalize(this)來告訴GC,讓它不用再呼叫物件的解構函式中。那麼改寫後的AA的dispose方法如下:
 

        public void Dispose()

        {

            fs.Dispose();

            MessageBox.Show("dispose執行了");

GC.SuppressFinalize(this);
        }

本文下載自CSDN下載頻道。

 -------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

那.Net程式設計師在程式設計時應該怎麼做,有沒有一種既簡單又有有效的方法來處理內在回收。愚人作以下建議,望各路高手不吝賜教:

       1,對於不包涵或沒有引用(直接或間接)非託管資源的類,特別是作用如同Struct的實體類,析構、終結器、Dispose均不採用。

       2,對於包涵非託管資源的類,如資料庫連線物件,檔案控制代碼等,應繼承IDispose介面,在Dispose方法中清理非託管物件。客戶程式碼用using(…){}格式顯示呼叫Dispose。如果繼承了IDispose介面,Dispose方法就不要留空,這樣沒有任何意義。除了構造器,任何方法體留空都有害無益。

       3,所有自定義類一般均不建議顯式宣告解構函式、Finalize方法。