summaryrefslogtreecommitdiff
path: root/gc.c
diff options
context:
space:
mode:
authorYukihiro Matsumoto <matz@ruby-lang.org>1994-08-10 15:54:46 +0900
committerTakashi Kokubun <takashikkbn@gmail.com>2019-08-17 22:09:30 +0900
commit6e3090413652b6592346556149fed1e9aec5495d (patch)
treebac97139bbeedc8cb67cb2e451a22ed4ddb2b2d4 /gc.c
parent200e0ee2fd3c1c006c528874a88f684447215524 (diff)
version 0.50v0_50
http://cache.ruby-lang.org/pub/ruby/1.0/ruby-0.50.tar.gz Wed Aug 10 15:54:46 1994 Yukihiro Matsumoto (matz@ix-02) * variable.c: -vオプションが指定されている時は初期化されていない, 大域変数, インスタンス変数, ローカル変数を参照した時点でwarning を出すようにした. Tue Aug 9 11:50:48 1994 Yukihiro Matsumoto (matz@ix-02) * bignum.c: 冪乗に関しても多倍長演算を行なうように. 特に浮動小数点 数の範囲を越えた時の処理を的確に行なうように. * eval.c: メソッド定義後は構文木から, メソッド定義部分を外す. 無駄 な再定義が起こらないようにするためと2重にfree()されないため. * array.c(Fary_aref): 引数が1つでFixnumの時, Range checkを行なわな いように修正. * eval.c: 引数の数をコンパイル時に計算して若干の高速化. Mon Aug 8 13:06:24 1994 Yukihiro Matsumoto (matz@ix-02) * object.c: nilによる比較連鎖をなくした. * parse.y: bit演算子の優先順位を比較演算子よりも強くした. Cとは異 なることになるが, 直観には合致する. * gc.c: クラスを解放する時, 個々のメソッド毎にキャッシュをクリアす るのではなく, クラス単位でクリアするように. Thu Aug 4 18:45:09 1994 Yukihiro Matsumoto (matz@ix-02) * methods.c(method_free): 解放されたメソッドに関してキャッシュをク リアしておく必要があった. * gc.c: Dataクラスのデータ部分をfree()し忘れていた. Wed Aug 3 09:58:14 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y: def func .. end形式による関数メソッドの定義はなくなった. * methods.c: func形式のメソッドをなくした. あっても, あまり意味が ないので. * eval.c: $0への代入でps(1)の出力が変化するように. * io.c(Fsyscall): syscall()を実現. Mon Aug 1 13:41:11 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y: ダブルクォートで囲まれた文字列や正規表現中で"#{変数名}" または"#変数名"という形式で変数の内容を埋め込むことができるよう になった. * io.c: 関数メソッドsystem2()はなくなった. 今はバッククォートがあ るからね. * parse.y: `cmd`によってコマンドを文字列に展開することができるよう になった. * parse.y: __FILE__, __LINE__を追加. それぞれファイル名(文字列), 行番号(整数)を値とする疑似変数. Fri Jul 29 13:16:07 1994 Yukihiro Matsumoto (matz@ix-02) * methods.h: メソッドをオブジェクトとして扱うのをやめる. メソッド のメモリ管理にはリファレンスカウントを使うことにした. これでオブ ジェクトの数が減ってほんの少しだけGCが速くなる(かな). * purifyによってメモリ関係のバグを検査した(見つかる,見つかる…). * gc.c: GCをプログラマが変数をマークする形式から, スタックとレジス タからマークする方法に変更. 移植性が下がるような気もするが, siod やscmでも採用されているから多分大丈夫だろう. Linux on i486でも動 作を確認した. Wed Jul 27 16:13:13 1994 Yukihiro Matsumoto (matz@ix-02) * eval.c(Eval): トップレベルでは構造木をfreeしないように. どうせ解 放されるから時間の無駄である. * array.c, dict.c: "=="を構造一致に変更. Fri Jul 22 10:14:09 1994 Yukihiro Matsumoto (matz@ix-02) * error.c: 組み込みタイプの名前を登録し忘れていた. Thu Jul 21 14:06:48 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y(freenode),eval.c(Eval): 解析木を解放し忘れていた. Mon Jul 18 10:19:15 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y: 多重代入を処理するルールにバグがあって, 3要素以上の多重 代入に失敗していた. * eval.c(rb_eval): 多重代入で, 右辺が配列でない時には`to_a'メソッ ドで配列に変換して代入するようにした. 今までの仕様だと右辺値が第 1要素にそのまま代入されていたが, structなど配列に変換できるもの は変換した方が嬉しい気がする. * dbm.c,dict.c(delete_if): メソッド追加. * process.c(wait,waitpid): システムコールwaitpidまたはwait4がある 時はそちらを使うように. configureもそれらをチェックするように変更. * dbm.c, dict.c(clear): メソッド追加.
Diffstat (limited to 'gc.c')
-rw-r--r--gc.c522
1 files changed, 333 insertions, 189 deletions
diff --git a/gc.c b/gc.c
index 5f30206f09..96b886fa2f 100644
--- a/gc.c
+++ b/gc.c
@@ -14,16 +14,14 @@
#include "env.h"
#include "st.h"
#include <stdio.h>
+#include <setjmp.h>
void *malloc();
void *calloc();
void *realloc();
-struct gc_list *GC_List = Qnil;
-static struct gc_list *Global_List = Qnil;
-static unsigned long bytes_alloc = 0, gc_threshold = 1000000;
-
-static mark_tbl();
+void gc();
+void gc_mark();
void *
xmalloc(size)
@@ -31,12 +29,10 @@ xmalloc(size)
{
void *mem;
- bytes_alloc += size;
if (size == 0) size = 1;
mem = malloc(size);
if (mem == Qnil) {
gc();
- bytes_alloc += size;
mem = malloc(size);
if (mem == Qnil)
Fatal("failed to allocate memory");
@@ -75,23 +71,41 @@ xrealloc(ptr, size)
return mem;
}
-void
-rb_global_variable(var)
- VALUE *var;
-{
- struct gc_list *tmp;
-
- tmp = (struct gc_list*)xmalloc(sizeof(struct gc_list));
- tmp->next = Global_List;
- tmp->varptr = var;
- tmp->n = 1;
- Global_List = tmp;
-}
-
-static struct RBasic *object_list = Qnil;
-static struct RBasic *literal_list = Qnil;
-static unsigned long fl_current = FL_MARK;
-static unsigned long fl_old = 0L;
+/* The way of garbage collecting which allows use of the cstack is due to */
+/* Scheme In One Defun, but in C this time.
+
+ * COPYRIGHT (c) 1989 BY *
+ * PARADIGM ASSOCIATES INCORPORATED, CAMBRIDGE, MASSACHUSETTS. *
+ * ALL RIGHTS RESERVED *
+
+Permission to use, copy, modify, distribute and sell this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all copies
+and that both that copyright notice and this permission notice appear
+in supporting documentation, and that the name of Paradigm Associates
+Inc not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+PARADIGM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+PARADIGM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+gjc@paradigm.com
+
+Paradigm Associates Inc Phone: 617-492-6079
+29 Putnam Ave, Suite 6
+Cambridge, MA 02138
+*/
+
+#ifdef sparc
+#define FLUSH_REGISTER_WINDOWS asm("ta 3")
+#else
+#define FLUSH_REGISTER_WINDOWS /* empty */
+#endif
static int dont_gc;
@@ -113,23 +127,6 @@ Fgc_disable()
return old;
}
-VALUE
-Fgc_threshold(obj)
- VALUE obj;
-{
- return INT2FIX(gc_threshold);
-}
-
-VALUE
-Fgc_set_threshold(obj, val)
- VALUE obj, val;
-{
- int old = gc_threshold;
-
- gc_threshold = NUM2INT(val);
- return INT2FIX(old);
-}
-
#include <sys/types.h>
#include <sys/times.h>
@@ -145,140 +142,204 @@ static Fgc_end()
VALUE M_GC;
-static ID start_hook, end_hook;
+static struct gc_list {
+ int n;
+ VALUE *varptr;
+ struct gc_list *next;
+} *Global_List = Qnil;
-struct RBasic *
-newobj(size)
- unsigned long size;
+void
+rb_global_variable(var)
+ VALUE *var;
{
- struct RBasic *obj = Qnil;
+ struct gc_list *tmp;
- if (bytes_alloc + size > gc_threshold) {
- gc();
- }
- obj = (struct RBasic*)xmalloc(size);
- obj->next = object_list;
- object_list = obj;
- obj->flags = fl_current;
- obj->iv_tbl = Qnil;
+ tmp = (struct gc_list*)xmalloc(sizeof(struct gc_list));
+ tmp->next = Global_List;
+ tmp->varptr = var;
+ tmp->n = 1;
+ Global_List = tmp;
+}
- return obj;
+struct RVALUE {
+ union {
+ struct {
+ int flag; /* alway 0 for freed obj */
+ struct RVALUE *next;
+ } free;
+ struct RObject object;
+ struct RClass class;
+ struct RFloat flonum;
+ struct RString string;
+ struct RArray array;
+ struct RRegexp regexp;
+ struct RDict dict;
+ struct RData data;
+ struct RStruct rstruct;
+ struct RBignum bignum;
+ } as;
+} *freelist = Qnil;
+
+struct heap_block {
+ struct heap_block *next;
+ struct RVALUE *beg;
+ struct RVALUE *end;
+ struct RVALUE body[1];
+} *heap_link = Qnil;
+
+#define SEG_SLOTS 4000
+#define SEG_SIZE (SEG_SLOTS*sizeof(struct RVALUE))
+
+static int heap_size;
+
+static void
+add_heap()
+{
+ struct heap_block *block;
+ struct RVALUE *p, *pend;
+
+ block = (struct heap_block*)malloc(sizeof(*block) + SEG_SIZE);
+ if (block == Qnil) Fatal("cant alloc memory");
+ block->next = heap_link;
+ block->beg = &block->body[0];
+ block->end = block->beg + SEG_SLOTS;
+ p = block->beg; pend = block->end;
+ while (p < pend) {
+ p->as.free.flag = 0;
+ p->as.free.next = freelist;
+ freelist = p;
+ p++;
+ }
+ heap_link = block;
+ heap_size += SEG_SLOTS;
}
-literalize(obj)
- struct RBasic *obj;
+struct RBasic *
+newobj()
{
- struct RBasic *ptr = object_list;
+ struct RBasic *obj;
+ if (heap_link == Qnil) add_heap();
+ if (freelist) {
+ retry:
+ obj = (struct RBasic*)freelist;
+ freelist = freelist->as.free.next;
+ obj->flags = 0;
+ obj->iv_tbl = Qnil;
+ return obj;
+ }
+ if (dont_gc) add_heap();
+ else gc();
- if (NIL_P(obj) || FIXNUM_P(obj)) return;
+ goto retry;
+}
- FL_SET(obj, FL_LITERAL);
- if (ptr == obj) {
- object_list = ptr->next;
- obj->next = literal_list;
- literal_list = obj;
+VALUE
+newdata(size)
+ UINT size;
+{
+ extern VALUE C_Data;
+ struct RData *data = (struct RData*)newobj();
- return;
- }
+ OBJSETUP(data, C_Data, T_DATA);
+ data->data = xmalloc(size);
+ return (VALUE)data;
+}
- while (ptr && ptr->next) {
- if (ptr->next == obj) {
- ptr->next = obj->next;
- obj->next = literal_list;
- literal_list = obj;
+static struct literal_list {
+ VALUE val;
+ struct literal_list *next;
+} *Literal_List = Qnil;
- return;
- }
- ptr = ptr->next;
- }
- Bug("0x%x is not a object.", obj);
+void
+literalize(obj)
+ VALUE obj;
+{
+ struct literal_list *tmp;
+
+ tmp = (struct literal_list*)xmalloc(sizeof(struct literal_list));
+ tmp->next = Literal_List;
+ tmp->val = obj;
+ Literal_List = tmp;
}
void
unliteralize(obj)
- struct RBasic *obj;
+ VALUE obj;
{
- struct RBasic *ptr = literal_list;
+ struct literal_list *ptr = Literal_List, *tmp;
if (NIL_P(obj) || FIXNUM_P(obj)) return;
if (!FL_TEST(obj, FL_LITERAL)) return;
FL_UNSET(obj, FL_LITERAL);
- if (ptr == obj) {
- literal_list = ptr->next;
- goto unlit;
+ if (ptr->val == obj) {
+ Literal_List = ptr->next;
+ free(ptr);
+ return;
}
while (ptr->next) {
- if (ptr->next == obj) {
- ptr->next = obj->next;
+ if (ptr->next->val == obj) {
+ tmp = ptr->next;
+ ptr->next = ptr->next->next;
+ ptr = tmp;
+ free(tmp);
+ return;
}
ptr = ptr->next;
- goto unlit;
}
Bug("0x%x is not a literal object.", obj);
-
- unlit:
- obj->next = object_list;
- object_list = obj;
- obj->flags &= ~FL_MARK;
- obj->flags |= fl_current;
- return;
}
-extern st_table *rb_global_tbl;
extern st_table *rb_class_tbl;
+static VALUE *stack_start_ptr;
-gc()
+static long
+looks_pointerp(p)
+ struct RVALUE *p;
{
- struct gc_list *list;
- struct ENVIRON *env;
- int i, max;
-
- rb_funcall(M_GC, start_hook, 0, Qnil);
-
- if (dont_gc) return;
- dont_gc++;
- fl_old = fl_current;
- fl_current = ~fl_current & FL_MARK;
-
- /* mark env stack */
- for (env = the_env; env; env = env->prev) {
- mark(env->self);
- for (i=1, max=env->argc; i<max; i++) {
- mark(env->argv[i]);
- }
- if (env->local_vars) {
- for (i=0, max=env->local_tbl[0]; i<max; i++)
- mark(env->local_vars[i]);
- }
- }
-
- /* mark protected C variables */
- for (list=GC_List; list; list=list->next) {
- VALUE *v = list->varptr;
- for (i=0, max = list->n; i<max; i++) {
- mark(*v);
- v++;
- }
- }
-
- /* mark protected global variables */
- for (list = Global_List; list; list = list->next) {
- mark(*list->varptr);
+ struct heap_block *heap = heap_link;
+
+ if (FIXNUM_P(p)) return FALSE;
+ while (heap) {
+ if (heap->beg <= p && p < heap->end
+ && ((((char*)p) - ((char*)heap->beg)) % sizeof(struct RVALUE)) == 0)
+ return TRUE;
+ heap = heap->next;
}
+ return FALSE;
+}
- mark_global_tbl();
- mark_tbl(rb_class_tbl);
-
- mark_trap_list();
+static void
+mark_locations_array(x, n)
+ VALUE *x;
+ long n;
+{
+ int j;
+ VALUE p;
+ for(j=0;j<n;++j)
+ {p = x[j];
+ if (looks_pointerp(p)) {
+ gc_mark(p);
+ }
+ }
+}
- sweep();
- bytes_alloc = 0;
- dont_gc--;
+static void
+mark_locations(start, end)
+ VALUE *start, *end;
+{
+ VALUE *tmp;
+ long n;
- rb_funcall(M_GC, end_hook, 0, Qnil);
+ if (start > end) {
+ tmp = start;
+ start = end;
+ end = tmp;
+ }
+ n = end - start;
+ mark_locations_array(start,n);
}
static
@@ -286,7 +347,7 @@ mark_entry(key, value)
ID key;
VALUE value;
{
- mark(value);
+ gc_mark(value);
return ST_CONTINUE;
}
@@ -302,8 +363,8 @@ mark_dicentry(key, value)
ID key;
VALUE value;
{
- mark(key);
- mark(value);
+ gc_mark(key);
+ gc_mark(value);
return ST_CONTINUE;
}
@@ -314,39 +375,35 @@ mark_dict(tbl)
st_foreach(tbl, mark_dicentry, 0);
}
-mark(obj)
+void
+gc_mark(obj)
register struct RBasic *obj;
{
if (obj == Qnil) return;
if (FIXNUM_P(obj)) return;
- if ((obj->flags & FL_MARK) == fl_current) return;
+ if (obj->flags & FL_MARK) return;
- obj->flags &= ~FL_MARK;
- obj->flags |= fl_current;
+ obj->flags |= FL_MARK;
switch (obj->flags & T_MASK) {
case T_NIL:
case T_FIXNUM:
- Bug("mark() called for broken object");
+ Bug("gc_mark() called for broken object");
break;
}
if (obj->iv_tbl) mark_tbl(obj->iv_tbl);
+ gc_mark(obj->class);
switch (obj->flags & T_MASK) {
- case T_OBJECT:
- mark(obj->class);
- break;
case T_ICLASS:
- mark(RCLASS(obj)->super);
+ gc_mark(RCLASS(obj)->super);
if (RCLASS(obj)->c_tbl) mark_tbl(RCLASS(obj)->c_tbl);
- mark_tbl(RCLASS(obj)->m_tbl);
break;
case T_CLASS:
- mark(RCLASS(obj)->super);
+ gc_mark(RCLASS(obj)->super);
case T_MODULE:
if (RCLASS(obj)->c_tbl) mark_tbl(RCLASS(obj)->c_tbl);
- mark_tbl(RCLASS(obj)->m_tbl);
- mark(RBASIC(obj)->class);
+ gc_mark(RBASIC(obj)->class);
break;
case T_ARRAY:
{
@@ -354,21 +411,21 @@ mark(obj)
VALUE *ptr = RARRAY(obj)->ptr;
for (i=0; i < len; i++)
- mark(ptr[i]);
+ gc_mark(ptr[i]);
}
break;
case T_DICT:
mark_dict(RDICT(obj)->tbl);
break;
case T_STRING:
- if (RSTRING(obj)->orig) mark(RSTRING(obj)->orig);
+ if (RSTRING(obj)->orig) gc_mark(RSTRING(obj)->orig);
break;
case T_DATA:
if (RDATA(obj)->dmark) (*RDATA(obj)->dmark)(DATA_PTR(obj));
break;
+ case T_OBJECT:
case T_REGEXP:
case T_FLOAT:
- case T_METHOD:
case T_BIGNUM:
break;
case T_STRUCT:
@@ -377,42 +434,77 @@ mark(obj)
struct kv_pair *ptr = RSTRUCT(obj)->tbl;
for (i=0; i < len; i++)
- mark(ptr[i].value);
+ gc_mark(ptr[i].value);
}
break;
default:
- Bug("mark(): unknown data type %d", obj->flags & T_MASK);
+ Bug("gc_mark(): unknown data type %d", obj->flags & T_MASK);
}
}
-sweep()
-{
- register struct RBasic *link = object_list;
- register struct RBasic *next;
-
- if (link && (link->flags & FL_MARK) == fl_old) {
- object_list = object_list->next;
- obj_free(link);
- link = object_list;
- }
+#define MIN_FREE_OBJ 512
- while (link && link->next) {
- if ((link->next->flags & FL_MARK) == fl_old) {
- next = link->next->next;
- obj_free(link->next);
- link->next = next;
- continue;
+static void
+gc_sweep()
+{
+ struct heap_block *heap = heap_link;
+ int freed = 0;
+
+ freelist = Qnil;
+ while (heap) {
+ struct RVALUE *p, *pend;
+ struct RVALUE *nfreelist;
+ int n = 0;
+
+ nfreelist = freelist;
+ p = heap->beg; pend = heap->end;
+ while (p < pend) {
+
+ if (!(RBASIC(p)->flags & FL_MARK)) {
+ if (RBASIC(p)->flags) obj_free(p);
+ p->as.free.flag = 0;
+ p->as.free.next = nfreelist;
+ nfreelist = p;
+ n++;
+ }
+ RBASIC(p)->flags &= ~FL_MARK;
+ p++;
+ }
+ if (n == SEG_SLOTS) {
+ struct heap_block *link = heap_link;
+ if (heap != link) {
+ while (link) {
+ if (link->next && link->next == heap) {
+ link->next = heap->next;
+ break;
+ }
+ link = link->next;
+ }
+ if (link == Qnil) {
+ Bug("non-existing heap at 0x%x", heap);
+ }
+ }
+ free(heap);
+ heap_size -= SEG_SLOTS;
+ heap = link;
}
- link = link->next;
+ else {
+ freed += n;
+ freelist = nfreelist;
+ }
+ heap = heap->next;
+ }
+ if (freed < heap_size/4) {
+ add_heap();
}
}
static
freemethod(key, body)
ID key;
- char *body;
+ void *body;
{
- freenode(body);
+ method_free(body);
return ST_CONTINUE;
}
@@ -432,6 +524,7 @@ obj_free(obj)
break;
case T_MODULE:
case T_CLASS:
+ rb_clear_cache2(obj);
st_foreach(RCLASS(obj)->m_tbl, freemethod);
st_free_table(RCLASS(obj)->m_tbl);
if (RCLASS(obj)->c_tbl)
@@ -452,14 +545,12 @@ obj_free(obj)
break;
case T_DATA:
if (RDATA(obj)->dfree) (*RDATA(obj)->dfree)(DATA_PTR(obj));
+ free(DATA_PTR(obj));
break;
case T_ICLASS:
/* iClass shares table with the module */
case T_FLOAT:
break;
- case T_METHOD:
- freenode(RMETHOD(obj)->node);
- break;
case T_STRUCT:
free(RSTRUCT(obj)->name);
free(RSTRUCT(obj)->tbl);
@@ -468,9 +559,69 @@ obj_free(obj)
free(RBIGNUM(obj)->digits);
break;
default:
- Bug("sweep(): unknown data type %d", obj->flags & T_MASK);
+ Bug("gc_sweep(): unknown data type %d", obj->flags & T_MASK);
+ }
+}
+
+void
+gc()
+{
+ struct literal_list *lit;
+ struct gc_list *list;
+ struct ENVIRON *env;
+ int i, max;
+ jmp_buf save_regs_gc_mark;
+ VALUE stack_end;
+
+ if (dont_gc) return;
+ dont_gc++;
+
+ /* mark env stack */
+ for (env = the_env; env; env = env->prev) {
+ gc_mark(env->self);
+ if (env->argv)
+ mark_locations_array(env->argv, env->argc);
+ if (env->local_vars)
+ mark_locations_array(env->local_vars, env->local_tbl[0]);
}
- free(obj);
+
+ FLUSH_REGISTER_WINDOWS;
+ /* This assumes that all registers are saved into the jmp_buf */
+ setjmp(save_regs_gc_mark);
+ mark_locations((VALUE*)save_regs_gc_mark,
+ (VALUE*)(((char*)save_regs_gc_mark)+sizeof(save_regs_gc_mark)));
+ mark_locations((VALUE*)save_regs_gc_mark,
+ sizeof save_regs_gc_mark/sizeof(VALUE));
+ mark_locations(stack_start_ptr, (VALUE*) &stack_end);
+#if defined(THINK_C)
+ mark_locations(((char*)stack_start_ptr + 2),
+ ((char*)&stack_end + 2));
+#endif
+
+ /* mark protected global variables */
+ for (list = Global_List; list; list = list->next) {
+ gc_mark(*list->varptr);
+ }
+
+ /* mark literal objects */
+ for (lit = Literal_List; lit; lit = lit->next) {
+ gc_mark(lit->val);
+ }
+
+ gc_mark_global_tbl();
+ mark_tbl(rb_class_tbl);
+
+ gc_mark_trap_list();
+
+ gc_sweep();
+ dont_gc--;
+}
+
+Init_stack()
+{
+ VALUE start;
+
+ stack_start_ptr = &start;
}
Init_GC()
@@ -479,12 +630,5 @@ Init_GC()
rb_define_single_method(M_GC, "start", gc, 0);
rb_define_single_method(M_GC, "enable", Fgc_enable, 0);
rb_define_single_method(M_GC, "disable", Fgc_disable, 0);
- rb_define_single_method(M_GC, "threshold", Fgc_threshold, 0);
- rb_define_single_method(M_GC, "threshold=", Fgc_set_threshold, 1);
- rb_define_single_method(M_GC, "start_hook", Fgc_begin, 0);
- rb_define_single_method(M_GC, "end_hook", Fgc_end, 0);
- rb_define_func(M_GC, "garbage_collect", gc, 0);
-
- start_hook = rb_intern("start_hook");
- end_hook = rb_intern("end_hook");
+ rb_define_method(M_GC, "garbage_collect", gc, 0);
}