BOEHM GC原理及总结
BOEHM GC原理
從上次分配原理中知道,給出一個指針,可以根據(jù)二級數(shù)組找到HBLKHDR的描述信息,根據(jù)HBLKHDR又能知道其對應(yīng)的OBJ大小。再根據(jù)指針對齊原理,知道指針會存儲在OBJ內(nèi)的地方(遍歷整個OBJ,將可能為指針的都拿出來檢查)。經(jīng)過這幾層可以得到引用樹。
具體實現(xiàn)為首先將所有線程暫停(pthread_kill SUSPEND,當(dāng)然不能把自己這個線程也掛起,相關(guān)宏為#define STOP_WORLD GC_stop_world)
然后訪問各個線程的堆棧頂及底部(GC創(chuàng)建每個線程時會獲取底部指針,而線程在處理SUSPEND信號時,會將當(dāng)前棧頂指針保存到GC_THREAD結(jié)構(gòu)中,每個線程都對應(yīng)一個GC_THREAD結(jié)構(gòu)),然后遍歷所有線程的棧內(nèi)存,按照指針存儲對齊原理。
獲取可能的指針(判定該指針是否是有效指針的方式是:BOEHM會保存自己從HEAP分配內(nèi)存的最低地址和最高地址,若判定的指針位于該區(qū)間,則認為其可能為一個指針),再通過二級數(shù)組獲取該指針對應(yīng)的HBLKHDR。
若未找到(這種屬于異常情況了,未找到,卻可能被引用,有可能是非BOEHM分配的內(nèi)存,則將該內(nèi)存對應(yīng)PAGE地址放入BLACKLIST中,不能用來分配了,否則有幾率存在碰撞可能,一般情況下很少會發(fā)生,這個應(yīng)該是BOEHM后來打的補丁,暫不關(guān)注)
找到HBLKHDR后,會檢查該PAGE是否為空閑狀態(tài)(已被分配到ok_freelist中后,PAGE會被設(shè)置為非空閑)
若為空閑,則忽略該指針(因為未指向有效對象),若是,則標(biāo)記該指針指向的OBJ為使用狀態(tài),并將對應(yīng)的標(biāo)記位設(shè)置為可用(每給出一個指針,都能根據(jù)HBLKHDR知道該OBJ的大小,因為其中存儲了hb_sz字段),然后再遍歷該OBJ,找出可能的指針,并一一標(biāo)記。
 在整個標(biāo)記過程開始前,GC會清除所有HBLKHDR的標(biāo)記字段(此時其他線程已暫停),所有的HBLKHDR有一個隊列頭,遍歷隊列能獲取到所有的HBLKHDR。然后標(biāo)記完成后,被標(biāo)記的說明存在引用,未被標(biāo)記的則會被釋放到ok_freelist,若整個HBLKHDR所有標(biāo)記都未被設(shè)置,則會將HBLKHDR還回到GC_hblkfreelist中,并且會根據(jù)其地址,將相鄰的PAGE合并成大PAGE再調(diào)整其在GC_hblkfreelist中的存儲位置。
這里介紹時忽略了其執(zhí)行的細節(jié)(因為研究時重點看了其管理內(nèi)存的方式,對于回收分為幾個過程,并通過管理回收狀態(tài)來標(biāo)記和清掃,并最終回收,整個過程的代碼較為晦澀,所以只管理其重點部分)。
相關(guān)堆棧
這里未研究其他全局數(shù)據(jù)GC方式,原理跟堆棧中引用類似。
總結(jié)
MONO按照16字節(jié)內(nèi)存的對齊方式分配內(nèi)存,每分配一個對象時,會根據(jù)其內(nèi)存大小并嘗試分配ok_freelist,若程序存在一堆小內(nèi)存對象,且其對象大小為16,32,48等內(nèi)存大小,則會生成三個ok_freelist鏈表,每個鏈表占用了一個PAGE,若這些對象數(shù)量不大,則會導(dǎo)致內(nèi)存的浪費。
將16和32字節(jié)大小的對象定義更大,讓其接近48字節(jié),這樣所有的小對象都未48字節(jié),反而能節(jié)省MONO內(nèi)存(因為只用分配一個PAGE即可以滿足這些小內(nèi)存對象的分配)。 定義的對象占用內(nèi)存盡量壓縮,因為GC時會遍歷整個OBJ,因此如果OBJ越大,那么GC訪問的內(nèi)存就會越多,當(dāng)然也就越慢,因此對象在能實現(xiàn)機制的前提下,越小越好。
 上述兩條看起來有些沖突,其表達的意思是:
對于一些小對象,盡量讓其按照某一個GRANULE對齊(當(dāng)然,若某種GRANULE的對象有很多,則不需要特地處理),盡量節(jié)省內(nèi)存(因為如果某個GRANULE的對象很少,則浪費了1整個PAGE);而GC會遍歷內(nèi)存,因此定義對象時,內(nèi)存能用更小的字節(jié)表達,則盡量小,一些字段順序進行調(diào)整能使得編譯時進行內(nèi)存對齊的開銷更小,有可能也能節(jié)省部分內(nèi)存。
相關(guān)資源及從系統(tǒng)分配內(nèi)存對齊方式
Mono官網(wǎng)?http://www.mono-project.com/docs/
 BOEHM項目官網(wǎng)?http://www.hboehm.info/
 其使用到的與從堆中分配內(nèi)存相關(guān)的函數(shù)為sbrk,同時也調(diào)用了一個不常用的函數(shù)mprotect,用來改變內(nèi)存段屬性(會用來作為輔助調(diào)試的方式),在預(yù)分配時先改為PROT_NONE,只有真正分配給應(yīng)用使用時,才設(shè)置為可訪問,這樣如果存在內(nèi)存越界等行為,可以在測試階段發(fā)現(xiàn)。GC_unix_sbrk_get_mem接口會調(diào)用sbrk,分配內(nèi)存前會保證分配給上層的地址是按照頁面對齊的,
其具體實現(xiàn)為:
因為其上面所描述的分配HBLKHDR描述PAGE塊時,基本前提是內(nèi)存塊按照PAGE_SIZE對齊。
 ?
CUBE調(diào)用的部分接口回顧
1.通過閱讀MONO GC算法,回頭再看CUBE調(diào)用的接口mono_object_is_alive,就比較容易理解了,是通過讀取二級數(shù)組獲取到對應(yīng)指針的HBLKHDR后,查找其中對應(yīng)的hb_mark BIT位,來判定某個OBJ是否存活;
2.Cube通過調(diào)用mono_object_is_alive來判定對象存活,然后該接口在sgen GC算法中已廢棄,若UNITY后續(xù)更新MONO版本,CUBE該功能將可能失效(需要用新的方式);
3.Mono_object_is_alive一定要在gc_collect()調(diào)用完成后才能調(diào)用,否則無法獲取到正確信息(因為只有GC完成后才能判定是否存活,在GC_malloc調(diào)用后,并不會設(shè)置HBLKHDR相應(yīng)的mark標(biāo)記(這么做應(yīng)該為了簡便,因為只有GC回收內(nèi)存時才需要標(biāo)記,分配內(nèi)存不標(biāo)記,待GC階段再標(biāo)記));
總結(jié)
以上是生活随笔為你收集整理的BOEHM GC原理及总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: iPhone手机拍的图片为什么电脑上打不
- 下一篇: 干货!STABLE - 一种无监督高鲁棒
