本文內容是學習CLR.via C#的21章後我的整理,有不足之處歡迎指導。算法
昨天是1024,coder的節日,我爲本身coder之路定下一句準則--保持學習,保持自信,保持謙遜,保持分享,越走越遠。性能
第一部分—基本原理思想學習
垃圾回收機制是針對託管堆而言。spa
不一樣於C的運行時堆,託管堆是內存是連續的,每次分配新內存,NextObjPtr指針只須要加上新分配內存塊大小便可。C運行時堆爲了維護鏈表的完整性,每當分配新的內存時,遍歷鏈表,一旦發現足夠大的內存塊,則拆分塊,修改節點中的指針。從託管堆中分配內存的速度,幾乎能夠與線程棧分配相媲美。線程
GC機制回收的就是託管堆中的垃圾對象。指針
第二部分—基本算法思想code
GC檢查託管堆中是否有再也不使用的對象。對象
那麼什麼是再也不使用的對象?遞歸
首先要解釋什麼是根(root)。每一個應用程序都有一組根,每一個根都是一個存儲位置,其中包含指向引用類型對象的一個指針。該指針要麼運用託管堆中的一個對象,要麼爲null。類型中定義的任何靜態字段被認爲是一個根,任何方法參數或者局部變量也被認爲是一個根。只有引用類型變量,才被認爲是根,值類型的變量永遠不被認爲是根。索引
GC開始執行時,假設全部對象都是垃圾。
GC沿着線程棧上檢查全部的根,若是發現了一個跟引用堆中的對象,則在這個對象的「同步索引字段」上開啓一位,也就是將這個bit設置爲1,也就是說這個對象被標記了。
GC就是這樣,以遞歸的方式遍歷全部可達的對象。可達的對象也就是說有根的對象,就是在標記階段被標記的,也就是本次不回收的。因此不可達的對象就被回收了。
進入第二階段--壓縮階段。
實際上此壓縮非彼壓縮,在這裏是指碎片整理,如何整理呢?若是發現曉得內存塊,GC忽略它們,若是發現大的,可用的連續內存塊,GC把非垃圾對象移動到這裏以壓縮堆。
包含只想這些對象的指針的變量和CPU寄存器,如今都會變得無效,NextObjPtr也應從新指向託管堆的結尾。
第三部分—終結列表和F-reachable隊列
說到終結列表要從某些不只佔用內存的對象提及,好比FileStream,它不只佔用內存資源,也在佔用本地資源。
Finalize方法就是用於釋放本地資源的方法。
那麼這個終結列表用來作什麼呢?微軟固然不會多此一舉,請仔細看好下面這段:
既佔用內存資源,又佔用本地資源的,在GC回收這樣對象所佔的內存時,僅僅回收內存時不夠的,由於必定要調用Finalize方法來釋放本地資源啊!強烈不建議在代碼中咱們手動Finalize,這須要堆Finalize的實現有至關深入而且全面的理解。那麼微軟的GC是什麼怎麼來給咱們執行的呢?這就用到了終結列表,爲了必定要保證執行Finalize,在最初咱們new操做符分配內存地時候,若是該對象的類型中定義了Finalize方法,那麼將該對象的一個指針方法到終結列表當中,當此類對象在託管堆中斷定爲垃圾的時候,GC掃描終結列表,以查找這些對象的指針,該指針會從終結列表中移除,並追加到F-reachable隊列。
那麼這個隊列幹嗎的呢?我認爲惟一的目的就是復活對象,並調用Finalize方法釋放本地資源(若是對象是死的,咱們沒法調用其方法),調用方法的是一個微軟定義好的優先級比較高的一個線程,據說這樣作有不少好處。那麼爲何放到F-reachable隊列中就復活了?f(finalization)終結,reachable可達的。換言之,可將這個隊列看作靜態字段那樣的一個跟。
第四部分—代的思想和原理
GC機制不管什麼時候,都分爲三代,0代,1代,2代。
代是什麼,微軟關於這個作了假設,新對象生存週期比較短,而老對象傾向於活的久一些。所在代越高的對象,存活的越久,所在代越低的對象,越容易被回收。
代就是託管堆中被分配的內存而已,也能夠說把託管堆分紅三部分吧?
0代初始的預算大小爲256kb,當0代中的內存用完時,爲新對象分配內存,0代內存不夠用時,GC開始回收第一代,未被標記的對象固然回收,已標記的對象則這些對象提升一代,進入1代區域,與此同理,當一代內存已滿時,回收1代,此時根對象也就是標記的對象提高至2代。
再次重點說一下三代預算大小分別爲256KB,2MB,10MB,預算大小以提高性能爲宜,預算越大,垃圾回收頻率越低。再次注意的是,性能提高的理論源於開始的假設:新對象生存期較短,老對象傾向於活的久一些。請仔細看下面一段。
若是GC回收後,0代存活下來的對象不多,或者說回收的內存不少。0代預算可能會從256調整爲128。代的分配空間減小,意味着回收頻繁回收頻繁,但GC所作的工做會減小,從而減少進程的工做集,最理想的狀態是0代對象都是當作垃圾被回收,這樣沒必要壓縮內存,NextObjPtr指向0代起始處。這樣來說,最開始的假設是是成立的。