[ruby-dev:42520] GC issues
From:
SASADA Koichi <ko1@...>
Date:
2010-11-03 08:59:45 UTC
List:
ruby-dev #42520
ささだです。
GC まわりを見てたんですが、いくつか疑問点がありました。
1. ファイナライザの起動タイミング
現在、ファイナライザは after_gc_sweep() のタイミングでしか起動されませ
ん。つまり、すべての sweep が完了するまで遅延するわけですが、これは資源
解放のタイミングを遅くすることになるので、まずいような気がします。
これを、slot_sweep のたびにファイナライザを呼ぶようにしたら、メモリが
少ない環境でも、test-all が最後まで動くようになりました。
2. GC.stress が true 時の挙動
GC.stress が true の時は、mark/free を行い、オブジェクト空間が(それな
りに)綺麗になっている、ということを期待しているように思いますが、現在は
gc_lazy_sweep() を行うだけなので、まだ sweep が完了していない場合は、
mark などは起こりません。
とりあえず 1 は問題だろうと思います。
あと、諸々見ていった結果のパッチ。
Index: gc.c
===================================================================
--- gc.c (revision 29675)
+++ gc.c (working copy)
@@ -1043,12 +1043,25 @@ rb_during_gc(void)
#define RANY(o) ((RVALUE*)(o))
-static VALUE
-rb_newobj_from_heap(rb_objspace_t *objspace)
+VALUE
+rb_newobj(void)
{
+ rb_objspace_t *objspace = &rb_objspace;
VALUE obj;
- if ((ruby_gc_stress && !ruby_disable_gc_stress) || !freelist) {
+ if (during_gc) {
+ dont_gc = 1;
+ during_gc = 0;
+ rb_bug("object allocation during garbage collection phase");
+ }
+
+ if (ruby_gc_stress && !ruby_disable_gc_stress) {
+ if (!garbage_collect(objspace)) {
+ during_gc = 0;
+ rb_memerror();
+ }
+ }
+ if (!freelist) {
if (!gc_lazy_sweep(objspace)) {
during_gc = 0;
rb_memerror();
@@ -1068,20 +1081,6 @@ rb_newobj_from_heap(rb_objspace_t *objsp
return obj;
}
-VALUE
-rb_newobj(void)
-{
- rb_objspace_t *objspace = &rb_objspace;
-
- if (during_gc) {
- dont_gc = 1;
- during_gc = 0;
- rb_bug("object allocation during garbage collection phase");
- }
-
- return rb_newobj_from_heap(objspace);
-}
-
NODE*
rb_node_newnode(enum node_type type, VALUE a0, VALUE a1, VALUE a2)
{
@@ -1947,6 +1946,11 @@ slot_sweep(rb_objspace_t *objspace, stru
objspace->heap.free_num += free_num;
}
objspace->heap.final_num += final_num;
+
+ if (final_num > 0) {
+ /* kick finalizer */
+ RUBY_VM_SET_FINALIZER_INTERRUPT(GET_THREAD());
+ }
}
static int
@@ -1976,6 +1980,11 @@ before_gc_sweep(rb_objspace_t *objspace)
}
objspace->heap.sweep_slots = heaps;
objspace->heap.free_num = 0;
+
+ /* sweep unlinked method entries */
+ if (GET_VM()->unlinked_method_entry_list) {
+ rb_sweep_method_entry(GET_VM());
+ }
}
static void
@@ -1995,20 +2004,9 @@ after_gc_sweep(rb_objspace_t *objspace)
}
malloc_increase = 0;
- if (deferred_final_list) {
- /* clear finalization list */
- RUBY_VM_SET_FINALIZER_INTERRUPT(GET_THREAD());
- }
- else{
free_unused_heaps(objspace);
}
- /* sweep unlinked method entries */
- if (th->vm->unlinked_method_entry_list) {
- rb_sweep_method_entry(th->vm);
- }
-}
-
static int
lazy_sweep(rb_objspace_t *objspace)
{
@@ -2785,17 +2783,20 @@ run_single_final(VALUE arg)
}
static void
-run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE objid, VALUE table)
+run_finalizer(rb_objspace_t *objspace, VALUE objid, VALUE table)
{
long i;
int status;
VALUE args[3];
- args[1] = 0;
- args[2] = (VALUE)rb_safe_level();
- if (!args[1] && RARRAY_LEN(table) > 0) {
+ if (RARRAY_LEN(table) > 0) {
args[1] = rb_obj_freeze(rb_ary_new3(1, objid));
}
+ else {
+ args[1] = 0;
+ }
+
+ args[2] = (VALUE)rb_safe_level();
for (i=0; i<RARRAY_LEN(table); i++) {
VALUE final = RARRAY_PTR(table)[i];
args[0] = RARRAY_PTR(final)[1];
@@ -2828,7 +2829,7 @@ run_final(rb_objspace_t *objspace, VALUE
key = (st_data_t)obj;
if (st_delete(finalizer_table, &key, &table)) {
- run_finalizer(objspace, obj, objid, (VALUE)table);
+ run_finalizer(objspace, objid, (VALUE)table);
}
}
@@ -2843,17 +2844,10 @@ finalize_deferred(rb_objspace_t *objspac
}
}
-static void
-gc_finalize_deferred(rb_objspace_t *objspace)
-{
- finalize_deferred(objspace);
- free_unused_heaps(objspace);
-}
-
void
rb_gc_finalize_deferred(void)
{
- gc_finalize_deferred(&rb_objspace);
+ finalize_deferred(&rb_objspace);
}
static int
@@ -2919,7 +2913,7 @@ rb_objspace_call_finalizer(rb_objspace_t
st_foreach(finalizer_table, force_chain_object, (st_data_t)&list);
while (list) {
struct force_finalize_list *curr = list;
- run_finalizer(objspace, curr->obj, rb_obj_id(curr->obj), curr->table);
+ run_finalizer(objspace, rb_obj_id(curr->obj), curr->table);
st_delete(finalizer_table, (st_data_t*)&curr->obj, 0);
list = curr->next;
xfree(curr);
@@ -2973,7 +2967,8 @@ rb_gc(void)
{
rb_objspace_t *objspace = &rb_objspace;
garbage_collect(objspace);
- gc_finalize_deferred(objspace);
+ finalize_deferred(objspace);
+ free_unused_heaps(objspace);
}
/*
見ながらメモっていったもの。似たような関数があって、ちょっとわからなく
なったので整理。
slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
あるスロットについて、sweep を行う。sweep_slot にあったものを取ってくる。
ready_to_gc(rb_objspace_t *objspace)
GC をやって良ければ(dont_gc || during_gc)TRUE を返す
もし、やっちゃ駄目で freelist が 0 なら、heaps を拡張して対応。
before_gc_sweep(rb_objspace_t *objspace)
mark 終了後、sweep を開始する前処理。
after_gc_sweep(rb_objspace_t *objspace)
sweep 終了後の後処理。
lazy_sweep(rb_objspace_t *objspace)
freelist に何かしらの値が入るまで、lazy sweep を進める。
進めても、freelist に値が入っていなければ、FALSE を返す。
rest_sweep(rb_objspace_t *objspace)
まだ sweep していない slot が残っていれば、それをすべて sweep する。
gc_lazy_sweep(rb_objspace_t *objspace)
Lazy sweep 版 garbage_collect
newobj は、この関数を呼ぶ
- もし、sweep の途中であれば、lazy_sweep() する
freelist が埋まれば、そこで返す
- mark をする
- 最初の1個のオブジェクトが見つかるまで、sweep をする
みつかった時点で、sweep 中断
gc_sweep(rb_objspace_t *objspace)
全部 sweep する
------------------------------------------
finalize_list(rb_objspace_t *objspace, RVALUE *p)
p は RVALUE の連結リスト。このリストはファイナライザ対象。
run_final(rb_objspace_t *objspace, VALUE obj)
obj の後始末を行う
- T_DATA の場合は登録された free 関数を呼ぶ
- Ruy レベルでファイナライザが登録されていれば、それを呼ぶ(run_finalizer)
run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE objid, VALUE table)
Ruby レベルで登録されたファイナライザを起動する
- ファイナライザは複数登録されているかも。それが talbe という配列。
- これ、obj 要らないじゃん。
- table って関数名は変な感じ。
run_single_final(VALUE arg)
ファイナライザを一つ起動する。run_finalizerから、rb_protec で呼ばれる。
finalize_deferred(rb_objspace_t *objspace)
遅延されたファイナライザを起動する。
- deferred_final_list で示される連結リスト
- 起動する前に、deferred_final_list をクリア
gc_finalize_deferred(rb_objspace_t *objspace)
ファイナライザを呼んで、その後使われていないヒープを回収。
- finalize_deferred(objspace);
- free_unused_heaps(objspace);
同じようなこと関数が多いので、これは廃止したほうが良さそう。
rb_gc_finalize_deferred(void)
gc_finalize_deferred を、gc.c から呼ぶためのインターフェース。
thread.c で、割り込み処理のところから使われている。
- free_unused_heaps は呼ばなくていいんではないか?
つまり、after_gc_sweep だけで呼べばいいと思われる。
--
// SASADA Koichi at atdot dot net