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