RubyのGCについて

最近はGC周りのことについての知識欲求が強い. ちょこっとCRuby(MRI)の内部を触ったので覚え書き程度に書き残す. 何で検索にひっかかってるのかよく見ていないが,ちょこちょこアクセスがあったのが不思議に感じた(記事を書くキッカケ).

ソースを触る環境について

MRIをソースからビルドする場合,macbrewを使っているならばパスの通り方を気をつけた方がよい. brewでlibtoolとかが入ってるとコンパイルが通らないため,注意. ソースからビルドに関しては上記以外は特に気にする必要はないが,make minirubyとかで良い.

ヒープについて

MRIのヒープは昔の記事で色々見かけるが,特に大きな変更はされていない. つまり,heapsと呼ばれるheapのリストの集まりが存在し,さらにheapの中にslotと呼ばれるobjectのリスト集合が存在する. 言っている意味がわからない場合はAoki Mineroさんのwebページを参考にすると良い.

イメージは上で良いが,実際にはheaps変数は存在しない. 実際は*objspaceでヒープヘッダを押さえていて,内部にはヒープのエントリやオプションが記載されている.

profile部分の情報をカットした構造体定義が以下になる. さらっと見るだけでもRGenGCやインクリメンタルGCの表記が見え不穏さを感じる…… さてRubyは保守的なGCとどうやって接しているのか…

みたいな形で記事は次へ(不定期)

少し脇道に逸れるが,ソース内の命名方法が特殊で変数っぽい小文字の場合でもマクロだったりするときがある. つまりソースを読んでいるときに急に変な名前が飛び出すことがある. 個人的にはマクロは大文字が良いなあと思ってしまう.あとは変換すれば良い話だがインデントが水平タブとスペースが混在しているのが気になる… インデントに関してはもしかして自分の環境だけかもしれない.

typedef struct rb_objspace {
    struct {
    size_t limit;
    size_t increase;
#if MALLOC_ALLOCATED_SIZE
    size_t allocated_size;
    size_t allocations;
#endif
    } malloc_params;

    struct {
    unsigned int mode : 2;
    unsigned int immediate_sweep : 1;
    unsigned int dont_gc : 1;
    unsigned int dont_incremental : 1;
    unsigned int during_gc : 1;
    unsigned int gc_stressful: 1;
    unsigned int has_hook: 1;
#if USE_RGENGC
    unsigned int during_minor_gc : 1;
#endif
#if GC_ENABLE_INCREMENTAL_MARK
    unsigned int during_incremental_marking : 1;
#endif
    } flags;

    rb_event_flag_t hook_events;
    size_t total_allocated_objects;

    rb_heap_t eden_heap;
    rb_heap_t tomb_heap; /* heap for zombies and ghosts */

    struct {
    rb_atomic_t finalizing;
    } atomic_flags;

    struct mark_func_data_struct {
    void *data;
    void (*mark_func)(VALUE v, void *data);
    } *mark_func_data;

    mark_stack_t mark_stack;
    size_t marked_slots;

    struct {
    struct heap_page **sorted;
    size_t allocated_pages;
    size_t allocatable_pages;
    size_t sorted_length;
    RVALUE *range[2];
    size_t freeable_pages;

    /* final */
    size_t final_slots;
    VALUE deferred_final;
    } heap_pages;

    st_table *finalizer_table;

    struct {
    int run;
    int latest_gc_info;
    gc_profile_record *records;
    gc_profile_record *current_record;
    size_t next_index;
    size_t size;
    double invoke_time;

#if USE_RGENGC
    size_t minor_gc_count;
    size_t major_gc_count;

#endif /* USE_RGENGC */

    /* temporary profiling space */
    double gc_sweep_start_time;
    size_t total_allocated_objects_at_gc_start;
    size_t heap_used_at_gc_start;

    /* basic statistics */
    size_t count;
    size_t total_freed_objects;
    size_t total_allocated_pages;
    size_t total_freed_pages;
    } profile;
    struct gc_list *global_list;

    VALUE gc_stress_mode;

#if USE_RGENGC
    struct {
    VALUE parent_object;
    int need_major_gc;
    size_t last_major_gc;
    size_t uncollectible_wb_unprotected_objects;
    size_t uncollectible_wb_unprotected_objects_limit;
    size_t old_objects;
    size_t old_objects_limit;

#if RGENGC_ESTIMATE_OLDMALLOC
    size_t oldmalloc_increase;
    size_t oldmalloc_increase_limit;
#endif

    } rgengc;
#if GC_ENABLE_INCREMENTAL_MARK
    struct {
    size_t pooled_slots;
    size_t step_slots;
    } rincgc;
#endif
#endif /* USE_RGENGC */
} rb_objspace_t;