C#的GC機制(來自網摘複製,未整理)

來源:互聯網
上載者:User

標籤:

第一個就是很多人用.Net寫程式,會談到託管這個概念。那麼.Net所指的資源託管到底是什麼意思,是相對於所有資源,還是只限於某一方面資源?很多人對此不是很瞭解,其實.Net所指的託管只是針對記憶體這一個方面,並不是對於所有的資源;因此對於Stream,資料庫的串連,GDI+的相關對象,還有Com對象等等,這些資源並不是受到.Net管理而統稱為非託管資源。而對於記憶體的釋放和回收,系統提供了GC-Garbage Collector,而至於其他資源則需要手動進行釋放。

那麼第二個概念就是什麼是垃圾,通過我以前的文章,會瞭解到.Net類型分為兩大類,一個就是實值型別,另一個就是參考型別。前者是分配在棧上,並不需要GC回收;後者是分配在堆上,因此它的記憶體釋放和回收需要通過GC來完成。GC的全稱為“Garbage Collector”,顧名思義就是記憶體回收行程,那麼只有被稱為垃圾的對象才能被GC回收。也就是說,一個參考型別對象所佔用的記憶體需要被GC回收,需要先成為垃圾。那麼.Net如何判定一個參考型別對象是垃圾呢,.Net的判斷很簡單,只要判定此對象或者其包含的子物件沒有任何引用是有效,那麼系統就認為它是垃圾。

明確了這兩個基本概念,接下來說說GC的運作方式以及其的功能。記憶體的釋放和回收需要伴隨著程式的運行,因此系統為GC安排了獨立的線程。那麼GC的工作大致是,查詢記憶體中對象是否成為垃圾,然後對垃圾進行釋放和回收。那麼對於GC對於記憶體回收採取了一定的優先演算法進行輪循回收記憶體資源。其次,對於記憶體中的垃圾分為兩種,一種是需要調用對象的解構函式,另一種是不需要調用的。GC對於前者的回收需要通過兩步完成,第一步是調用對象的解構函式,第二步是回收記憶體,但是要注意這兩步不是在GC一次輪循完成,即需要兩次輪循;相對於後者,則只是回收記憶體而已。

那麼對於程式資源來說,我們應該做些什麼,以及如何去做,才能使程式效率最高,同時佔用資源能儘快的釋放。前面也說了,資源分為兩種,託管的記憶體資源,這是不需要我們操心的,系統已經為我們進行管理了;那麼對於非託管的資源,這裡再重申一下,就是Stream,資料庫的串連,GDI+的相關對象,還有Com對象等等這些資源,需要我們手動去釋放。

如何去釋放,應該把這些操作放到哪裡比較好呢。.Net提供了三種方法,也是最常見的三種,大致如下:
<!--[if !supportLists]-->1.  <!--[endif]-->解構函式;
<!--[if !supportLists]-->2.  <!--[endif]-->繼承IDisposable介面,實現Dispose方法;
<!--[if !supportLists]-->3.  <!--[endif]-->提供Close方法。

經過前面的介紹,可以知道解構函式只能被GC 來調用的,那麼無法確定它什麼時候被調用,因此用它作為資源的釋放並不是很合理,因為資源釋放不及時;但是為了防止資源泄漏,畢竟它會被GC 調用,因此解構函式可以作為一個補救方法。而Close 與Dispose 這兩種方法的區別在於,調用完了對象的Close 方法後,此對象有可能被重新進行使用;而Dispose 方法來說,此對象所佔有的資源需要被標記為無用了,也就是此對象被銷毀了,不能再被使用。例如,常見SqlConnection這個類,當調用完Close方法後,可以通過Open重新開啟資料庫連接,當徹底不用這個對象了就可以調用Dispose方法來標記此對象無用,等待GC回收。明白了這兩種方法的意思後,大家在往自己的類中添加的介面時候,不要歪曲了這兩者意思。 GC為了提高回收的效 率使用了Generation的概念,原理是這樣的,第一次回收之前建立的對象屬於Generation 0,之後,每次回收時這個Generation的號碼就會向後挪一,也就是說,第二次回收時原來的Generation 0變成了Generation 1,而在第一次回收後和第二次回收前建立的對象將屬於Generation 0。GC會先試著在屬於Generation 0的對象中回收,因為這些是最新的,所以最有可能會被回收,比如一些函數中的局部變數在退出函數時就沒有引用了(可被回收)。如果在Generation 0中回收了足夠的記憶體,那麼GC就不會再接著回收了,如果回收的還不夠,那麼GC就試著在Genera

在C#中,資源分為託管資源和非託管資源兩種。GC在回收無用對象資源時,可以自動回收託管資源(比如託管記憶體),但對於非託管資源(比如Socket、檔案、資料庫連接)必須在程式中顯式釋放。

託管資源的回收首先需要GC識別無用對象,然後回收其資源。一般無用對象是指通過當前的系統根對象和呼叫堆疊對象不可達的對象。對象有一個重要的特點導致無用對象判斷的複雜性:對象間的相互引用!如果沒有相互引用,就可以通過“引用計數”這種簡單高效的方式實現無用對象的判斷,並實現即時回收。正是由於相互引用的存在導致GC需要設計更為複雜的演算法,這樣帶來的最大問題在於喪失了資源回收的即時性,而變成一種不確定的方式。

對於非託管資源的釋放,C#提供了兩種方式:

1.Finalizer:寫法貌似C++的解構函式,本質上卻相差甚遠。Finalizer是對象被GC回收之前調用的終結器,初衷是在這裡釋放非託管資源,但由於GC運行時機的不確定性,通常會導致非託管資源釋放不及時。另外,Finalizer可能還會有意想不到的副作用,比如:被回收的對象已經沒有被其他可用對象所引用,但Finalizer內部卻把它重新變成可用,這就破壞了GC垃圾收集過程的原子性,增大了GC開銷。

2.Dispose Pattern:C#提供using關鍵字支援Dispose Pattern進行資源釋放。這樣能通過確定的方式釋放非託管資源,而且using結構提供了異常安全性。所以,一般建議採用Dispose Pattern,並在Finalizer中輔以檢查,如果忘記顯式Dispose對象則在Finalizer中釋放資源。

可以說,GC為程式帶來安全方便的同時也付出了不小的代價:一則喪失了託管資源回收的即時性,這在即時系統和資源受限系統中是致命的;二則沒有把C#託管資源和非託管資源的管理統一起來,造成概念割裂。C++的定位之一是底層開發能力,所以不難理解GC並沒有成為C++的語言特性。雖然我們在C++0x和各種第三方庫都能看到GC的身影,但GC對於C++來講並不是那麼重要,至多是一個有益的補充。C++足以傲視C,並和C# GC一較高下的是它的RAII。以上介紹C#託管資源和非託管資源

 

 

 

 

首先:談談託管,什麼叫託管,我的理解就是託付C#運行環境幫我們去管理,在這個運行環境中可以協助我們開闢記憶體和釋放記憶體,開闢記憶體一般用new,記憶體是隨機分配的,釋放主要靠的是GC也就是記憶體回收機制。哪麼有兩個大問題  1.GC可以回收任何對象嗎? 2.GC什麼時候來回收對象?回收那些對象?對於第一個問題,GC可以回收任何對象嗎?我是這樣理解的,首先要明白一點,C#在強大也管不到Unmanaged 程式碼?哪麼什麼是Unmanaged 程式碼呢?比如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清理垃圾,實際上是調用解構函式,但是這些代碼是Managed 程式碼(因為裡面沒有涉及到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();

}哪麼在點擊第一次按鈕的時候,產生四個對象,然後強制記憶體回收,這個時候,會回收嗎?當然不會,因為,這四個對象還在執行中(方法還沒結束),當點第二次按鈕的時候,會出現四次"解構函式被執行了",這是在釋放第一次點擊按鈕的四個對象,然後以後每次點擊都會出現四次"解構函式被執行了",哪麼最後一次的對象什麼時候釋放的,在關閉程式的時候釋放(因為關閉程式要釋放所有的記憶體)。 好了,現在來談談Unmanaged 程式碼,剛才說過,Unmanaged 程式碼不能由記憶體回收釋放,我們把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不是Managed 程式碼,所以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);

        }

 

 

C#技術漫談之記憶體回收機制(GC)_知識庫_部落格園
http://kb.cnblogs.com/page/106720/

 

 

 

 

 

 

 

 

C#的GC機制(來自網摘複製,未整理)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.