在初學階段用.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,提升編程效率之類的問題。