GC)が導入されました。今までのGCに対してどのように世代別GCが導入されたのか、Restrictedってどういう意味なのかなどについて見ていくことにしましょう。対象としたのは2.1.1です。2.1.2出てますが読み始めたときは2.1.1だったので2.1.1で読解していきます。†gc.cを上から見ていくとまずRGenGCのパラメータをdefineしているところがあります。配布されているソースそのままだと各値は以下のようになるようです。1パラメータのうち上3つはデバッグ・プロファイリング用、下2つがGC動作用のようです。†いつもながらにrb_newobj()から見ていきます。下請けのnewobj_of()中に以下のコードがあります。heap_eden);2.1以降、heapは複数持たれるようになったようです。heap_edenが通常のheap、もう一つのheap_tombはページ内のオブジェクトが全部使われなくなった(死んだ)際のつなぎ先のようです。すると解放候補から1個だけ取られるということがなくなるから効率が良くなるのかな?RGenGCとは関係のない変更のようです。heap_get_freeobj()。}}2.0でslotごとにfreelistを管理するようになったはずですが見た感じ全体のfreelistに戻ってます。ソースを流し読みした感じでは通常、heap->freelistはNULLなようでelseの方が実行されるみたいです。p;}heap_pageは2.0までのheaps_slotと同じようなものです。freeなページを取り出してusingにつなぎかえ、freeなページがない場合はheap_prepare_freepage()呼んで確保を行っています。長いので一部端折ります。†garbage_collect_body()に進む。プロファイル系のコードとかを消去すると以下のような感じ。TRUE;}RGenGCに関するパラメータが出てきました。need_major_gcに値が設定されているときshadyオブジェクトがリミットを超えたときoldオブジェクトがリミットを超えたときそれぞれの条件に関する記述は今まで見てきた中にはまだ出てきていません。gc_marks()に進む。prev_mark_func_data;}とりあえずリミットに関する処理が出てきました。GCを行うということになっているようです。続いてgc_marks_body()。GCかで処理が分かれています。GCの方を見ることにしましょう。†というわけでrgengc_mark_and_rememberset_clear()。}}mark_bitsは2.0から導入されたビットマップマーキングに関するものです。remembersetって何でしょうか?ということで先に進みます。GCの場合はマークをクリアしないことが予想されます。gc_mark_roots()、gc_mark_stacked_objects()は毎度おなじみのルートオブジェクトのマーク、スタックに積んだオブジェクトの再帰的マーキングです。末端のgc_mark()を見てみましょう。}}2.0のころと比べると、rgengc_check_relation()が増えています。rgengcのプレフィクスが示す通りRGenGCに関する処理のようです。}}親がoldかつ自身がライトバリア保護されていない場合、rememberとされています。remember、つまり、覚えておく、です。覚えておくってどういうことなのかわからないのでgc_mark_children()を見てみましょう。(以下、従来通り)infantは幼児、つまり、最後にGCしてから生まれたオブジェクトです。それがルートオブジェクトからたどれるということは生き残ったということなのでoldオブジェクトということになります。ifの場合もelseの場合もparent_object_is_oldをTRUEにするなどコードが冗長になっているのは実際には3世代GCのコードが書かれているためです。RVALUE_PROMOTE_INFANT()をデバッグコード省略して2世代GCに限定すると以下のような感じ。†さて、というわけでオブジェクトがoldに設定される個所は確認できました。GCの場合について見ていきましょう。今まで見てきた中でmajor/minorが分岐していたところはどこだったでしょうか。gc_mark_stacked_objects(objspace);}です。during_minor_gcがTRUE(full_markがFALSE)を見ると前にも見たremembersetに関する処理がされてそうです。この部分がポイントではあるのですがその前になんでこれでmajor/minorが分けれてるかを確認しましょう。2.0から、オブジェクトのマークとはビットマップ中のオブジェクトに対応するビットを立てることになりました。ビットが立っていたら改めてそれ以降(フィールド)のマーキングはしません。(gc_mark_ptr()参照)ところで、full_markがTRUEの場合はマークされてるかビットをクリア(全部再確認)しますが、FALSEの場合はしません。GCが起こるまではフィールドのチェックは行われません。これでオブジェクトが生きているかの確認処理を削減することができます。生きているとされているオブジェクトは実は死んでるかもしれませんが、一度生き残ったオブジェクトは長生きする(一時オブジェクトはすぐ死ぬ→回収される)という経験則を利用しています。GCで)回収されます。以上、終わり、とできれば話は楽なのですがこれだけだと問題があります。それを説明するのがめんどくさい、というかさわだが参考にしたサイトに説明を丸投げします。読んで理解したら戻ってきてくださいね(ぉ戻ってきていただきありがとうございます。謎のremembersetとはnewなオブジェクトを参照してしまったoldオブジェクトです。GCの場合に呼び出していたrgengc_rememberset_mark()ではremembersetに入れられたオブジェクトのマーキングを行っています。†では残りの問題を片付けましょう。残っているのは、ライトバリアです。参考サイトでは次の2つのことが述べられていました。newなオブジェクトを参照したoldはrembersetに送られるRARRAY_PTRマクロを使われるなど追っかけきれない場合はshady(WB_PROTECTEDではない)化するそれぞれ見ていきましょう。†オブジェクト間の参照関係ということで、Array#[]=を実装しているrb_ary_aset()を見てみましょう。ソースはarray.cに移ります。本当はもう少しいろいろやっていますがばっさり削って載せます。val);}RARRAY_ASETはruby.hに書かれているマクロです。(0)RB_OBJ_WRITEマクロはファイル名と行番号を追加して同じくruby.hに書かれているrb_obj_write()を呼び出します。a;}rb_obj_written()もruby.hに書かれています。a;}a、フィールドを変更しようとしたオブジェクトがoldだったらrb_gc_writebarrier()を呼び出しています。ここでgc.cに戻ります。}}仕様通り、newなオブジェクトを参照したoldがremembersetに送られています。Hashのサイズが65536以上だったらのコードは、親をrememberに入れてしまうとマーク時間が長くなってしまうからというハックっぽいですね。思ったら2.1.2ではなくなってるようです。最後にRARRAY_PTRマクロを見てみましょう。((VALUE)a)))unprotectされています。追いかけてみましょう。x;}ここからgc.c。}}というわけでoldオブジェクトからshadyオブジェクトになりました。本当ならold→newの参照をする可能性のあるところにはすべてライトバリアを入れたいが、そのためには過去の拡張ライブラリを全ビルドしないといけないだったら、ライトバリアされてなくても動けるようにしてライトバリア対応されているものを徐々に増やしていこうというのが世代別GCを入れたけど、効果は限定されてますよ〜って意味なのかなと思います。†さて、2.1のGC(RGenGC)を見てきました。いうものかは先に挙げた参考サイトやささださんの発表資料を見てください(ぉRGenGCを読むにあたって1.9.3、2.0と順に読んできましたが無駄ではなかったなと思います。sweep、2.0で入ったビットマップマーキングを踏まえたうえでさらにGCの時間を短くするにはどうすればいいかということでRGenGCが導入されていました。GC実装の歴史を読むを終わりたいと思います。