ささだです.

 現在の GC 関連の状態を表示するためのメソッド GC.stat を追加するのはど
うでしょうか.

{:count=>1,                   # これまで GC が起こった回数
 :elapsed_time=>0.004,        # これまで GC にかかったユーザ時間
 :heap_used=>20,              # 利用中の heap の数
 :heap_length=>22,            # 確保した heap の数
 :heap_live_slot=>4742,     # 生きている slot の数 (*1)
 :heap_free_slot=>5056,     # 空いている slot の数 (*1)
 :heap_waiting_finalize_slot=>23} # ファイナライザを待っている slot の数

*1: 一番最後の GC でカウント.


# Gauche では gc-stat というものがある,と shiro さんに聞いたもので.
gosh> (gc-stat)
((:total-heap-size 786432) (:free-bytes 163840) (:bytes-since-gc 532376)
(:total-bytes 532352))


Index: gc.c
===================================================================
--- gc.c	(リビジョン 23689)
+++ gc.c	(作業コピー)
@@ -323,6 +323,9 @@ typedef struct rb_objspace {
 	size_t length;
 	size_t used;
 	size_t longlife_used;
+	size_t total_live_slot;
+	size_t total_free_slot;
+	size_t total_waiting_finalize_slot;
 	RVALUE *freelist;
 	RVALUE *longlife_freelist;
 	RVALUE *range[2];
@@ -355,7 +358,8 @@ typedef struct rb_objspace {
 	double invoke_time;
     } profile;
     struct gc_list *global_list;
-    unsigned int count;
+    size_t count;
+    double elapsed_time;
     int gc_stress;
 } rb_objspace_t;

@@ -933,7 +937,8 @@ assign_heap_slot(rb_objspace_t *objspace
     if (lomem == 0 || lomem > p) lomem = p;
     if (himem < pend) himem = pend;
     if (lifetime == lifetime_longlife) objspace->heap.longlife_used++;
-    heaps_used++;
+    objspace->heap.used++;
+    objspace->heap.total_free_slot += objs;

     while (p < pend) {
 	p->as.free.flags = 0;
@@ -1832,7 +1837,7 @@ static void
 gc_sweep(rb_objspace_t *objspace)
 {
     RVALUE *p, *pend, *final_list;
-    size_t freed = 0;
+    size_t freed = 0, waiting_finalize = 0;
     size_t i;
     size_t live = 0, free_min = 0, do_heap_free = 0;

@@ -1868,6 +1873,7 @@ gc_sweep(rb_objspace_t *objspace)
 		    p->as.free.next = final_list;
 		    final_list = p;
 		    final_num++;
+		    waiting_finalize++;
 		}
 		else {
 		    add_freelist(objspace, &freelist, p);
@@ -1877,6 +1883,7 @@ gc_sweep(rb_objspace_t *objspace)
 	    else if (BUILTIN_TYPE(p) == T_ZOMBIE) {
 		/* objects to be finalized */
 		/* do nothing remain marked */
+		waiting_finalize++;
 	    }
 	    else {
 		RBASIC(p)->flags &= ~FL_MARK;
@@ -1923,6 +1930,10 @@ gc_sweep(rb_objspace_t *objspace)
 	free_unused_heaps(objspace);
 	GC_PROF_SET_HEAP_INFO;
     }
+
+    objspace->heap.total_live_slot = live;
+    objspace->heap.total_free_slot = freed;
+    objspace->heap.total_waiting_finalize_slot = waiting_finalize;
 }

 static void
@@ -2207,6 +2218,8 @@ garbage_collect(rb_objspace_t *objspace)
 {
     struct gc_list *list;
     rb_thread_t *th = GET_THREAD();
+    double invoke_time = getrusage_time();
+
     INIT_GC_PROF_PARAMS;

     if (GC_NOTIFY) printf("start garbage_collect()\n");
@@ -2286,6 +2299,8 @@ garbage_collect(rb_objspace_t *objspace)

     GC_PROF_TIMER_STOP;
     if (GC_NOTIFY) printf("end garbage_collect()\n");
+    objspace->elapsed_time += getrusage_time() - invoke_time;
+
     return Qtrue;
 }

@@ -2950,6 +2965,33 @@ gc_count(VALUE self)
     return UINT2NUM((&rb_objspace)->count);
 }

+/*
+ *  call-seq:
+ *     GC.stat -> Hash
+ *
+ *  Return information about GC.
+ *
+ *  It returns several information about GC:
+ *    total count, elapesed time, heap size, and so on.
+ *
+ */
+static VALUE
+gc_stat(VALUE self)
+{
+    VALUE hash = rb_hash_new();
+    rb_objspace_t *objspace = &rb_objspace;
+
+    rb_hash_aset(hash, ID2SYM(rb_intern("count")),
SIZET2NUM(objspace->count));
+    rb_hash_aset(hash, ID2SYM(rb_intern("elapsed_time")),
DBL2NUM(objspace->elapsed_time));
+    rb_hash_aset(hash, ID2SYM(rb_intern("heap_used")),
SIZET2NUM(objspace->heap.used));
+    rb_hash_aset(hash, ID2SYM(rb_intern("heap_length")),
SIZET2NUM(objspace->heap.length));
+    rb_hash_aset(hash, ID2SYM(rb_intern("heap_live_slot")),
SIZET2NUM(objspace->heap.total_live_slot));
+    rb_hash_aset(hash, ID2SYM(rb_intern("heap_free_slot")),
SIZET2NUM(objspace->heap.total_free_slot));
+    rb_hash_aset(hash, ID2SYM(rb_intern("heap_waiting_finalize_slot")),
SIZET2NUM(objspace->heap.total_waiting_finalize_slot));
+
+    return hash;
+}
+
 #if CALC_EXACT_MALLOC_SIZE
 /*
  *  call-seq:
@@ -3122,6 +3164,7 @@ Init_GC(void)
     rb_define_singleton_method(rb_mGC, "stress", gc_stress_get, 0);
     rb_define_singleton_method(rb_mGC, "stress=", gc_stress_set, 1);
     rb_define_singleton_method(rb_mGC, "count", gc_count, 0);
+    rb_define_singleton_method(rb_mGC, "stat", gc_stat, 0);
     rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);

     rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");

-- 
// SASADA Koichi at atdot dot net