Sorry, I lied... >STACK_END is more far from rb_gc_stack_start on 1.times { pause } >(same thing happens on loop { pause }) this means ruby uses more stack >on block execution. > >Because rb_gc_mark_locations marks all objects in the range of STACK_END to rb_gc_stack_start. >if GC.start runs inside block, block needs more stack, so more objects can be wrongly >marked as alive. (As you can see, last `pause` is outside of block, so less stack is used, >huge array goes out of stack range, it is freed) Index: gc.c =================================================================== RCS file: /src/ruby/gc.c,v retrieving revision 1.168.2.37 diff -u -w -b -p -r1.168.2.37 gc.c --- gc.c 13 Feb 2006 09:10:53 -0000 1.168.2.37 +++ gc.c 28 Feb 2006 08:34:43 -0000 @@ -594,6 +594,18 @@ gc_mark_rest() } } +static VALUE watching = 1; + +#define GET_WATCHING() (watching - 1) +#define SET_WATCHING(value) (watching = value + 1) + +static VALUE +rb_gc_watch(VALUE self, VALUE obj) +{ + SET_WATCHING(obj); + return Qnil; +} + static inline int is_pointer_to_heap(ptr) void *ptr; @@ -620,8 +632,14 @@ mark_locations_array(x, n) register long n; { VALUE v; + while (n--) { v = *x; + if (GET_WATCHING()) { + if (GET_WATCHING() == v) { + printf("---> %p\n", x); + } + } if (is_pointer_to_heap((void *)v)) { gc_mark(v, 0); } @@ -1148,6 +1166,12 @@ obj_free(obj) break; } + if (GET_WATCHING()) { + if (GET_WATCHING() == obj) { + SET_WATCHING(0); + } + } + if (FL_TEST(obj, FL_EXIVAR)) { rb_free_generic_ivar((VALUE)obj); } @@ -1354,6 +1378,9 @@ garbage_collect() setjmp(save_regs_gc_mark); mark_locations_array((VALUE*)save_regs_gc_mark, sizeof(save_regs_gc_mark) / sizeof(VALUE *)); #if STACK_GROW_DIRECTION < 0 + if (GET_WATCHING()) { + printf("=============> %p %p\n", STACK_END, rb_gc_stack_start); + } rb_gc_mark_locations((VALUE*)STACK_END, rb_gc_stack_start); #elif STACK_GROW_DIRECTION > 0 rb_gc_mark_locations(rb_gc_stack_start, (VALUE*)STACK_END + 1); @@ -1926,6 +1953,7 @@ Init_GC() rb_define_singleton_method(rb_mGC, "enable", rb_gc_enable, 0); rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0); rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0); + rb_define_singleton_method(rb_mGC, "watch", rb_gc_watch, 1); rb_mObSpace = rb_define_module("ObjectSpace"); rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1); /////////////////////////////////////// def format(num) num.to_s.gsub(/(\d{1,3})(?=\d{3}+$)/) { $1 + "," } end def count(type) count = 0 capacity = 0 ObjectSpace.each_object(type) do |o| count += 1 capacity += o.capacity end puts "#{type}" puts " count = #{format(count)}" puts " capacity = #{format(capacity)}" end def pause(run_gc = true) GC.enable GC.start GC.disable count(String) count(Array) puts sleep 5 end class A def run arr = [] GC.watch(arr) 800000.times { arr << "d" * 7 } pause end end GC.disable A.new.run 1.times { pause } pause /////////////////////////////////////// E:\ruby-cvs\win32_1_8>miniruby \a.rb =============> 0012E4AC 0012FFFC ---> 0012E5D4 ---> 0012E738 ---> 0012EFF8 ---> 0012F630 String count = 800,111 capacity = 5,602,208 =============> 0012DD00 0012FFFC ---> 0012F630 # shalow enough ( > 0012ED38 ) String count = 800,115 capacity = 5,602,270 =============> 0012ED38 0012FFFC String count = 112 capacity = 2,214