CLR探索系列:GC 中的Card table和Brick Table(垃圾回收系列)
在CLR的垃圾回收子系統(tǒng)中,Card Table和Brick Table是兩個(gè)比較有意思的表。
在GC的過(guò)程中,一個(gè)Heap在運(yùn)行了一段時(shí)間以后,已經(jīng)分配的空間就會(huì)越來(lái)越大。在進(jìn)行了一次局部代或者是完全的垃圾回收以后,就會(huì)涉及到一個(gè)GC堆的類似碎片整理的概念。整理優(yōu)化一次GC Heap。同時(shí),這種機(jī)制保證了譬如一個(gè)IIS Server在長(zhǎng)時(shí)間的運(yùn)行過(guò)程中的穩(wěn)定性并且優(yōu)化了其內(nèi)存管理。
這樣的好處是顯而易見(jiàn)的,但是采用這種解決方案帶來(lái)的問(wèn)題也很容易想到:譬如一個(gè)存在于GC Heap里面的"Small" Object被移動(dòng)了,同時(shí)它也被其它的object引用。出現(xiàn)了這種情況,在進(jìn)行GC的時(shí)候,就需要遍歷整個(gè)GC的各種table,root,heap以及保留塊來(lái)搜索對(duì)這個(gè)對(duì)象的引用,然后更新這個(gè)引用。
這是一個(gè)相當(dāng)消耗CPU資源的動(dòng)作,幸運(yùn)的是,GC使用了Card table來(lái)減小這個(gè)動(dòng)作帶來(lái)的系統(tǒng)資源消耗。Card Table縮小了在這個(gè)搜索遍歷的過(guò)程中需要遍歷的范圍。
Card table實(shí)際上是一塊連續(xù)的內(nèi)存區(qū)域,做為一個(gè)index,這塊內(nèi)存區(qū)域里面的每一個(gè)bit,都代表了GC Heap中的一塊連續(xù)的區(qū)域,這些bits就組成了一個(gè)card table。
CLR的實(shí)現(xiàn)中,Card Table中的一個(gè)bit就代表了GC heap里面的128byte的區(qū)域。同時(shí),對(duì)Card Table的update的動(dòng)作是以byte為單位的。這就造成了每一次對(duì)Card Table的Update,影響到的其實(shí)是128*8=1kb這么大的一個(gè)區(qū)域。
在DotNet的中間層,但凡涉及到修改一個(gè)Object的Ref的CIL Opcodes, 不僅僅會(huì)執(zhí)行份內(nèi)的修改ref的事情,同時(shí)還會(huì)很小心的update這個(gè)Card Table。
基于這種設(shè)計(jì),在GC的時(shí)候,就可以先查找Card Table來(lái)看看哪些Object的Ref被修改了,然后只是針對(duì)這些區(qū)域去進(jìn)行搜索遍歷來(lái)Update GC Heap中其它的地方對(duì)這個(gè)對(duì)象的引用。
這些Bit位的Update,可以由WriteBarrierHelper和ErectWriteBarrier 這兩個(gè)方法來(lái)完成:
//update一個(gè)Card table的方法。
void GCHeap::ErectWriteBarrier(OBJECTREF *dst, OBJECTREF ref)
{
// 檢查dst的地址是不是在heap之內(nèi)的地址。
if (((*(BYTE**)&dst) < g_lowest_address) || ((*(BYTE**)&dst) >= g_highest_address))
return;
if((BYTE*) OBJECTREFToObject(ref) >= g_ephemeral_low
&& (BYTE*) OBJECTREFToObject(ref) < g_ephemeral_high)
{
size_t card = gcard_of((BYTE*)dst);
BYTE* pCardByte = ((BYTE*) g_card_table) + card / CARDS_PER_BYTE;
//每一次update是以一個(gè)byte為單位的。
BYTE bitMask = (BYTE) (1 << (card % 8));
if( !((*pCardByte) & bitMask) )
{
//如果這個(gè)有ref被修改,那么這個(gè)card的一個(gè)byte里面都被置為1
*pCardByte = 0xFF;
}
}
}
同時(shí),在update一個(gè)bit的時(shí)候,還使用了一種叫做wirte barrier的技術(shù)。這種技術(shù),在程序修改一個(gè)ref的內(nèi)容的時(shí)候,可以被編譯器得知。這個(gè)技術(shù)在card update里面,具體到某個(gè)平臺(tái)上面是一段匯編的代碼,其實(shí)個(gè)人認(rèn)為就是對(duì)CIL代碼的一個(gè)擴(kuò)展。
Bricks和card差不多。它的最主要的作用,是Collector用來(lái)定位一個(gè)Object在Heap里面的位置。它的表示形式,是一個(gè)16位有符號(hào)int類型的數(shù)組。每一個(gè)單元叫做brick slot,cover2048byte的內(nèi)存區(qū)域。對(duì)于每一個(gè)Brick slot,每一個(gè)16bit的成員都可以有三種表現(xiàn)形式:
1. 十六位的正數(shù)表示偏移。
2. 一個(gè)負(fù)數(shù)表示相對(duì)于Brick table本身的唯一。
3. 保留值,做為一個(gè)標(biāo)志位來(lái)使用。
另外,這兩種結(jié)構(gòu),都不能保證完全表現(xiàn)一個(gè)Heap里面的狀態(tài)信息。
posted on 2008-02-22 09:05 lbq1221119 閱讀(3396) 評(píng)論(7) 收藏 舉報(bào)
浙公網(wǎng)安備 33010602011771號(hào)