Rubyは如何にしてヒープを開放したか

第5章 ガ−ベージコレクション
これを読んだ人は分かる通り、実は今、ヒープ領域は開放されている
(つまりfreeされている)
 
どうやって開放しているか書かれていないので概念だけさらっと書く。
 
ヒープの構造は

となっているが、最新ではちょっと違う。
単純な配列の配列ではなく、配列の中にHeapの構造体が入っている。
 

static struct heaps_slot {
    //これがHeapの実態    
    void *membase;

  //先頭のオブジェクト
    RVALUE *slot;

    //Heap一列の長さ
    int limit;
} *heaps;

 
これが大きく変わった点の一つ。
 
それで前はオブジェクトの中の文字列(char*)とかとかを解放するだけだったが、
今はRVALUE自体を解放している。
 
さて、どうやっているのか?
gc_sweepメソッドを見てみることにする。
関係ない部分は省いておいた。

static void
gc_sweep(void)
{
    RVALUE *p, *pend, *final_list;
    int freed = 0;  //freeObjCnt
    int i;
    unsigned long live = 0;      //liveObjCnt
    unsigned long free_min = 0;

    
    //all heap limit ++
    for (i = 0; i < heaps_used; i++) {
        free_min += heaps[i].limit;
    }
    //Heap全体の個数の2割設定
    free_min = free_min * 0.2;
    ・
    ・
    //heapの一列をなめていく
    while (p < pend) {
    ・
     /* 解放・マーク消し処理 *///(A)このheap列全体がGC対象の場合 and 今までのGCmarks all > free_min
	if (n == heaps[i].limit && freed > free_min) {
	    RVALUE *pp;
            //(B)あとで大事になる
	    heaps[i].limit = 0;

	    //この列でのfinalizeするObjectにSINGLETONフラグ付加
	    for (pp = final_list; pp != final; pp = pp->as.free.next) {
		p->as.free.flags |= FL_SINGLETON; /* freeing page mark */
	    }

	    //この列でつなげたfreelistを消す
	    freelist = free;	/* cancel this page from freelist */
	}
	else {
	    freed += n;
	}
    }

    //heapの開放処理
    free_unused_heaps();
}

(A)の部分で解放するヒープの配列を決定している。
つまり、ヒープの配列丸ごと一本解放する。
では実際に解放しているメソッドを見てみる。
 

static void
free_unused_heaps(void)
{
    int i, j;

    for (i = j = 1; j < heaps_used; i++) {
        //Heapsがgc_sweepで削除すると決められたとき(B)
        if (heaps[i].limit == 0) {
            free(heaps[i].membase);
            heaps_used--;
        }
        else {
          //もし解放されればHeapを一つずつ詰めて行く
            if (i != j) {
                heaps[j] = heaps[i];
            }
            j++;
        }
    }
}

 
なるほど。
そういうことか。
全てがオブジェクトであるRubyでは(数値とか違うけど)使い捨てのObjectがバンバンでるので。
というか構文木ですらRubyObjだし、実際に解放されることは多々あると思われる。
 
世代別GCを導入するとライトバリアのコストが上回ってしまって、遅くなるのかも知れない。
それが、RubyGCのよくもないし、悪くもない原因なんだろうな。