程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 關於C#中垃圾回收GC雜談

關於C#中垃圾回收GC雜談

編輯:C#入門知識

在初學階段用.Net編寫程序時,一直都未曾考慮過程序垃圾資源回收率的問題,那是因為老師老在課堂講什麼不用管,不用理會,一聽到不用理會,好吧,從此寫程序就肆無忌憚的了!程序卡死、內存暴漲、順便偶爾來幾個內存錯誤,一看到這個就頭大了。現在想想,課堂老師講的那句話,卻只聽進了前半句。。。

閒聊無事,也不用再怕什麼在職防止洩露啥啥機密、啥啥技術的、、、嘎嘎、、、、(下面的純屬個人觀點,如有雷同、敬請繞道、、、)

在.Net裡面垃圾收集的工作方式:

運行.NET應用程序時,程序創建出來的對象實例都會被CLR跟蹤,CLR都是有記錄哪些對象還會被用到(存在引用關系);哪些對象不會再被用到(不存在引用關系)。CLR會整理不會再被用到的對象,在恰當的時機,按一定的規則銷毀部分對象,釋放出這些對象所占用的內存。

說到這裡,那就引出了新的技術點:

CLR是怎麼記錄對象引用關系的?

CLR會把對象關系做成一個“樹圖”,這樣標記他們的引用關系

CLR是怎麼釋放對象的內存的?

關鍵的技術是:CLR把沒用的對象轉移到一起去,使內存連續,新分配的對象就在這塊連續的內存上創建,這樣做是為了減少內存碎片。注意!CLR不會移動大對象

垃圾收集器按什麼規則收集垃圾對象?

CLR按對象在內存中的存活的時間長短,來收集對象。時間最短的被分配到第0代,最長的被分配到第2代,一共就3代。

一般第0貸的對象都是較小的對象,第2代的對象都是較大的對象,第0代對象GC收集時間最短(毫秒級別),第2代的對象GC收集時間最長。當程序需要內存時(或者程序空閒的時),GC會先收集第0代的對象,

收集完之後發現釋放的內存仍然不夠用,GC就會去收集第1代,第2代對象。(一般情況是按這個順序收集的)

如果GC跑過了,內存空間依然不夠用,那麼就拋出了OutOfMemoryException異常。

GC跑過幾次之後,第0代的對象仍然存在,那麼CLR會把這些對象移動到第1代,第1代的對象也是這樣。

既然有了垃圾收集器,為什麼還要Dispose方法和析構函數?

因為CLR的緣故,GC只能釋放托管資源,不能釋放非托管資源(數據庫鏈接、文件流等)。

那麼該如何釋放非托管資源呢?

一般我們會選擇為類實現IDispose接口,寫一個Dispose方法。

讓調用者手動調用這個類的Dispose方法(或者用using語句塊來自動調用Dispose方法)

Dispose執行時,析構函數和垃圾收集器都還沒有開始處理這個對象的釋放工作

有時候,我們不想為一個類型實現Dispose方法,我們想讓他自動的釋放非托管資源。那麼就要用到析構函數了。

析構函數是個很奇怪的函數,調用者無法調用對象的析構函數,析構函數是由GC調用的。

你無法預測析構函數何時會被調用,所以盡量不要在這裡操作可能被回收的托管資源,析構函數只用來釋放非托管資源

GC釋放包含析構函數的對象,比較麻煩(需要干兩次才能干掉她),

CLR會先讓析構函數執行,再收集它占用的內存。

我們需要手動執行垃圾收集嗎?什麼場景下這麼做?

GC什麼時候執行垃圾收集是一個非常復雜的算法(策略)

大概可以描述成這樣:

如果GC發現上一次收集了很多對象,釋放了很大的內存,

那麼它就會盡快執行第二次回收,

如果它頻繁的回收,但釋放的內存不多,

那麼它就會減慢回收的頻率。

所以,盡量不要調用GC.Collect()這樣會破壞GC現有的執行策略。

除非你對你的應用程序內存使用情況非常了解,你知道何時會產生大量的垃圾,那麼你可以手動干預垃圾收集器的工作

我有一個大對象,我擔心GC要過很久才會收集他,

關於弱引用和垃圾收集之間的關系?

當一個大對象被使用後不存在引用關系時,GC就會自動回收它占用的內存。

當這個對象足夠大的情況下,GC在回收它時,可能時間稍微會長點,當用戶需要再次使用該對象時,我們可以從回收池中再次提取該對象,這裡就涉及到弱引用,代碼如下:

var bss = new BsCtl(BrowserContainer);
            var vbss = new WeakReference(bss);
            bss = null;
            BsCtl ok;            
            vbss.TryGetTarget(out ok);
            //如果沒有進行垃圾收集OK不會為NULL
            if (ok == null)
            {
                //如果已經進行了垃圾收集,就會執行這段代碼
                ok = new BsCtl(BrowserContainer);
            }


垃圾收集隨時可以收集bss對象,

如果收集了,就會進入if語句塊,如果沒有收集,就不會進入if語句塊,TryGetTarget(out ok)就成功把bss從垃圾堆裡撈回來了。

注意:這裡只說了短弱引用,沒有提及長弱引用,我覺得長弱引用使用的場景較少。

垃圾收集器優點:

因為我沒有很豐富的C/C++編程經驗,如果想談垃圾收集器的好處,那麼勢必要和C/C++這樣的較低級的語言對比。所以一般性的回答都是減少內存使用不當的BUG,提升編程效率之類的問題。




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