Rifat Shariyar等,Reference Counting Immix,2013算法
RC Immix算法將引用計數的一大缺點(吞吐量低)改善到了實用的級別。本算法改善了引用計數算法的「合併型引用計數法」和Immix組合起來使用。函數
Yossi Levanoni, Erez Petrank,2001指針
以前咱們說過,在吞吐量方面引用計數不如搜索型GC。其緣由是引用計數器頻繁增減。在引用計數中,每當對象之間的關係發生變化,對象的計數器就會發生變化。若是計數器頻繁發生增減,那麼寫入屏障執行的頻率就會增大,處理就會變得繁重。code
因而出現了一種新的方法,就是把注意力只放在初始和最後的狀態上,該期間內不對計數器進行修改。這就是合併型引用計數法(Coalesced Reference Counting)。在該方法中,即便指針發生改動,計數器也不會增減。指針改動時的信息會被註冊到更改緩衝區。對象
在合併型引用計數法中,要將指針發生改動的對象和其全部子對象註冊到更改緩衝區中。這項操做是經過寫入屏障來執行的。不過由於這個時候咱們不更新計數器,因此計數器的值會保持錯誤。blog
# 合併型引用計數法的寫入屏障 write_barrier_coalesced_RC(obj, field, dst){ if(!obj.dirty) register(obj) obj.field = dst }
寫入屏障負責檢查要改動的指針的對象obj的標識dirty是否註冊完畢。若是沒有註冊,就將其註冊到更改緩衝區($mod_buf)。執行註冊的方法是register函數。內存
# register register(obj){ if($mod_buf.size <= $mod_buf.used_sized) garbage_collect() entry.obj = obj foreach(child_ptr :children(obj)) if(*child_ptr != nil) push(entry.children, *child_ptr) push($mod_buf, entry) obj.dirty = true }
#garbage_collect garbage_collect(){ foreach(entry: $mod_buf) obj = entry.obj foreach(child:obj) inc_ref_cnt(child) foreach(child : entry.children) dec_ref_cnt(child) obj.dirty = false clear($mod_buf) }
RC Immix中,對象不只有計數器,線也有計數器,這樣就能夠獲悉線內是否存在活動對象。不過線的計數器和對象有所不一樣。對象的計數器表示的是指向這個對象的指針數。而線的計數器則是表示這個線裏存活的對象數量。若是變成了0就將整個線回收。資源
對象生成和廢棄的頻率要低於對象間引用關係變化的頻率,這樣一來 更新計數器所產生的額外負擔就小了。get
下面來講一下RC Immix中是如何以線爲單位進行內存管理的。
dec_ref_cnt(obj){ obj.ref_cnt -- if(obj.ref_cnt ==0) reclaim_obj(obj) line = get_line(obj) line.ref_cnt-- if(line.ref_cnt == 0 ) reclaim_line(line) }
當對象的計數器爲0的時候,對線的計數器進行減量。當線的計數器爲0 的時候,咱們就能夠回收整條線。
此方法雖然把合併型引用計數和Immix組合到了一塊兒,可是不能執行壓縮。
在壓縮中要進行復制對象的操做。要實現這些操做,不只要複製對象,還要將引用此對象的指針所有改寫。所以壓縮所需的信息這裏是不能提供的。
因而RC Immix經過限定對象來時實現壓縮。就是下面要說的新對象。
在RC Immix中,把沒有經歷過GC的對象稱爲新對象。即就是在上一次GC以後生成的對象。
RC Immix,在更改緩衝區滿了的時候會查找更改緩衝區,這時若是發現了新的對象,就把他複製到別的空間去。
咱們準備一個空的塊來當作目標空間。在複製過程當中目標空間滿了的狀況下,就採用一個空的塊。咱們不對舊對象執行被動的碎片整理。RC Immix中的 garbage_collect()函數代碼清單以下。
garbage_collect(){ dst_block = get_empty_block() foreach(entry :$mod_but) obj = entry.obj foreach(child_ptr :children(obj)) inc_ref_cnt(*child_ptr) if(!(*child_ptr).old) reactive_defrag(child_ptr, dst_block) foreach(child : entry.children) dec_ref_cnt(child) obj.dirty = false }
這裏的garbage_collect()函數和合並型引用計數法中的garbage_collect()函數很像。基本流程都是查找更改緩衝區,根據狀況增量或者減量操做。
不一樣的是這裏對新對象調用了reactive_defrag()函數,這就是被懂得碎片整理。那麼咱們來看看 reactive_defrag()函數。
reactive_defrag(ptr, dst_block){ obj = *ptr if(obj.copied) *ptr = obj.forwarding else if(obj.size>dst_block.free_size) dst_block = get_empty_block() new_obj = dst_block.free_top copy_data(obj, new_obj, obj.size) obj.forwarding = new_obj *ptr = new_obj obj.copied = true new_obj.old = true dst_block.free_top += obj.size dst_block.free_size += obj.size line = get_line(obj) line.ref_cnt++ }
被動的碎片整理的兩處缺陷
爲了解決這些問題,RC Immix中進行了被動的碎片處理以外,還進行了另外一項操做。也就是積極的碎片整理。