? 在C#中,內(nèi)存的是由CLR進行管理,而在C\C++中內(nèi)存是由程序員手動管理。這樣的弊端就很明顯:1.忘記釋放不需要的內(nèi)存毯焕,造成內(nèi)存泄露;2.嘗試訪問已釋放的內(nèi)存磺樱,造成內(nèi)存破壞纳猫,程序錯誤,進而造成安全漏洞竹捉。而C#中由系統(tǒng)管理芜辕,不需要的內(nèi)存,由CLR進行清理块差。清理不需要的內(nèi)存侵续,就是C#中的垃圾回收。
1.從托管堆分配資源
? 在CLR中,所有對象都是從托管堆中分配憨闰,在進程初始化時状蜗,CLR會為此進程劃分一份地址空間區(qū)域作為托管堆,并且CLR中維護一個指針NextObjPtr(初始化時鹉动,為此空間區(qū)域的基地址)诗舰,它指向下一個對象在托管堆中所要分配的地址。當此托管堆區(qū)域被非垃圾對象填滿時训裆,系統(tǒng)將分配更多地址空間眶根。32位系統(tǒng)最多分配1.5GB,64位系統(tǒng)最多分配8TB边琉。
? C#在初始化一個對象時有以下操作:
計算類型字段的所需字節(jié)數(shù);
加上對象的開銷所需字節(jié)數(shù)(類型對象指針,和同步塊索引);
CLR檢查區(qū)域是否有足夠的字節(jié)數(shù)分配給新對象;
? 在托管堆中一般連續(xù)分配的對象都有較強的聯(lián)系属百,所以連續(xù)的內(nèi)存地址會引用“局部化”獲得性能的提升,這樣使進程工作集會非常小变姨,應用程序只需要使用很少的內(nèi)存族扰,從而 提供速度。以上有個前提是內(nèi)存無限,現(xiàn)實中渔呵,CLR通過“垃圾回收”怒竿,去清理不需要的對象,來盡可能增加內(nèi)存使用效率扩氢。
2.垃圾回收算法
? 當程序調(diào)用new操作符時耕驰,沒有足夠的內(nèi)存空間來分配是,CLR就執(zhí)行垃圾回收录豺。
- 引用計數(shù)垃圾回收器算法:(在Microsoft的“組件對象模型”用的就是此算法)這種算法朦肘,在程序中對上的每個對象都維護著一個內(nèi)存字段來統(tǒng)計程序有多少“部分”正在使用對象。隨著每一“部分”到達代碼中某個不需要對象的地方双饥,就遞減對象的計數(shù)字段媒抠。計數(shù)字段變成0,對象就可以從內(nèi)存中刪除了咏花。這有個弊端趴生,在GUI應用程序中,窗口將容納對子UI元素的引用昏翰,而子UI元素將容納對父窗口的引用苍匆。這種引用會組織兩個對象的計數(shù)器達到0,所以這兩個對象永遠不會刪除矩父。
這種算法因為存在問題,所以CLR使用另一種“引用跟蹤算法”排霉。
-
引用跟蹤算法:此算法只關(guān)心引用類型的變量窍株,只有這種變量才會引用堆上的對象,這種變量稱為根攻柠。
(1)CLR開始執(zhí)行GC時球订,首先暫停進程中的所有線程(防止線程在檢查期間訪問對象并更改狀態(tài));
(2)CLR進入GC的標記階段瑰钮,CLR遍歷堆中的所有對象冒滩,將同步塊索引字段中的一位設為0(0表示對象應刪除);
(3)CLR檢查所有活動根浪谴,檢查他們引用了哪些對象(這就是引用跟蹤)开睡。如果根引用了堆上的對象,CLR就會標記被引用的對象苟耻,將該對象的同步塊索引中的位設為1篇恒,一個對象被標記后,CLR會檢查對象中的根凶杖,并標記他們引用的對象胁艰,如果發(fā)現(xiàn)對象已經(jīng)被標記,則不重新檢查對象的字段。(這就避免了引用計數(shù)垃圾回收器算法中的循環(huán)引用而產(chǎn)生的死循環(huán)腾么,導致對象無法被刪除)奈梳。
(4)檢查完畢后,同步塊索引中標記為0的被刪除解虱,標記為1的保留攘须。
(5)執(zhí)行GC中的壓縮階段,這個階段將保留的對象所占的內(nèi)存地址進行改變饭寺,使他們占用連續(xù)的內(nèi)存地址(好處:1.使保留的對象恢復引用“局部化”阻课,減小程序工作集,提升訪問這些對象的性能艰匙;2.使剩下的可用空間也是連續(xù)的限煞;3. 壓縮空間,解決堆中的空間碎片化問題)员凝。
垃圾回收示例.png
3.代:提升性能
? CLR中的GC是基于代的垃圾回收器:
- 對象越新署驻,生存周期越短;
- 對象越老,生存周期越長健霹;
- 回收堆的一部分旺上,速度快于回收整個堆。
其工作原理:
- 當程序首次運行時糖埋,新初始化的對象都為第0代對象宣吱;
- 當?shù)?代對象占用的空間達到CLR中的預算容量時(CLR初始化時,會為每代的對象設置一個預存容量)瞳别,GC開始執(zhí)行征候,利用引用跟蹤算法,將不可達的對象清理祟敛,而未清理的對象成為第1代對象疤坝。所以每次GC后,第0代就沒有對象了馆铁;
- 然后新創(chuàng)建的對象則會分配到0代中跑揉,循環(huán)上面的清理過程。
- 當?shù)?代對象占用的內(nèi)存空間達到預設容量時埠巨,GC就是利用引用跟蹤算法历谍,清理第1代對象,而未清理的對象則會升為第2代對象(第2代對象為GC中的最高代對象辣垒,GC中只包含3代對象)扮饶,在第1代對象沒有達到預設容量時,則不會管第1代中的對象乍构,就算在執(zhí)行GC時甜无,第1代中含有不可達對象扛点,也不會在此時清理。
- 當然CLR中對每代對象的預設對象不是恒定不變的岂丘,比如:當每次回收第0代對象后陵究,所存活的對象很少,則會減少第0代對象的內(nèi)存空間預設容量奥帘;反之铜邮,每次回收第0代對象后,所存的對象很多寨蹋,則會增加第0代對象的內(nèi)存空間預設容量松蒜。
一個新初始化的堆,其中包含了一些對象, 這些對象都是0代, 有顏色的是可達對象, 無顏色的是不可達的.
經(jīng)過一次垃圾回收, 第0代中的可達對象升級為第1代 ; 其他的被清除, 此時第0代沒有對象
第0代分配了新對象,第1代有垃圾產(chǎn)生(第1代內(nèi)存未達到預設值)
第2次回收后,第0代的可達對象升級為第1代, 之前第1代中的垃圾并沒有被清理
第1代產(chǎn)生更多垃圾, 第0代有新增對象
第3次回收, 因為第1代已達到預設值, 則將第1代可達對象升級為第2代, 第0代可達對象升級為第1代.(垃圾回收中 最高為第2代)
4.垃圾回收觸發(fā)條件
-
CLR在檢測到第0代超過預算時;
-
代碼顯示調(diào)用system.GC的靜態(tài)Collect方法;
-
Windows報告低內(nèi)存情況;
-
CLR正在卸載AppDomain;
-
CLR正在關(guān)閉.