Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals

gc.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   gc.c -
00004 
00005   $Author: akr $
00006   $Date: 2005/12/16 04:58:51 $
00007   created at: Tue Oct  5 09:44:46 JST 1993
00008 
00009   Copyright (C) 1993-2003 Yukihiro Matsumoto
00010   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00011   Copyright (C) 2000  Information-technology Promotion Agency, Japan
00012 
00013 **********************************************************************/
00014 
00015 #include "ruby.h"
00016 #include "rubysig.h"
00017 #include "st.h"
00018 #include "node.h"
00019 #include "env.h"
00020 #include "re.h"
00021 #include <stdio.h>
00022 #include <setjmp.h>
00023 #include <sys/types.h>
00024 
00025 #ifdef HAVE_SYS_TIME_H
00026 #include <sys/time.h>
00027 #endif
00028 
00029 #ifdef HAVE_SYS_RESOURCE_H
00030 #include <sys/resource.h>
00031 #endif
00032 
00033 #ifdef __ia64__
00034 #include <ucontext.h>
00035 #if defined(__FreeBSD__)
00036 /*
00037  * FreeBSD/ia64 currently does not have a way for a process to get the
00038  * base address for the RSE backing store, so hardcode it.
00039  */
00040 #define __libc_ia64_register_backing_store_base (4ULL<<61)
00041 #else
00042 #pragma weak __libc_ia64_register_backing_store_base
00043 extern unsigned long __libc_ia64_register_backing_store_base;
00044 #endif
00045 #endif
00046 
00047 #if defined _WIN32 || defined __CYGWIN__
00048 #include <windows.h>
00049 #endif
00050 
00051 void re_free_registers (struct re_registers*);
00052 void rb_io_fptr_finalize (struct OpenFile*);
00053 
00054 #if !defined(setjmp) && defined(HAVE__SETJMP)
00055 #define setjmp(env) _setjmp(env)
00056 #endif
00057 
00058 /* Make alloca work the best possible way.  */
00059 #ifdef __GNUC__
00060 # ifndef atarist
00061 #  ifndef alloca
00062 #   define alloca __builtin_alloca
00063 #  endif
00064 # endif /* atarist */
00065 #else
00066 # ifdef HAVE_ALLOCA_H
00067 #  include <alloca.h>
00068 # else
00069 #  ifdef _AIX
00070  #pragma alloca
00071 #  else
00072 #   ifndef alloca /* predefined by HP cc +Olibcalls */
00073 void *alloca ();
00074 #   endif
00075 #  endif /* AIX */
00076 # endif /* HAVE_ALLOCA_H */
00077 #endif /* __GNUC__ */
00078 
00079 #ifndef GC_MALLOC_LIMIT
00080 #if defined(MSDOS) || defined(__human68k__)
00081 #define GC_MALLOC_LIMIT 200000
00082 #else
00083 #define GC_MALLOC_LIMIT 8000000
00084 #endif
00085 #endif
00086 
00087 static unsigned long malloc_increase = 0;
00088 static unsigned long malloc_limit = GC_MALLOC_LIMIT;
00089 static void run_final();
00090 static VALUE nomem_error;
00091 static void garbage_collect();
00092 
00093 void
00094 rb_memerror()
00095 {
00096     static int recurse = 0;
00097 
00098     if (recurse > 0 && rb_safe_level() < 4) {
00099         fprintf(stderr, "[FATAL] failed to allocate memory\n");
00100         exit(1);
00101     }
00102     recurse++;
00103     rb_exc_raise(nomem_error);
00104 }
00105 
00106 void *
00107 ruby_xmalloc(size)
00108     long size;
00109 {
00110     void *mem;
00111 
00112     if (size < 0) {
00113         rb_raise(rb_eNoMemError, "negative allocation size (or too big)");
00114     }
00115     if (size == 0) size = 1;
00116     malloc_increase += size;
00117 
00118     if (malloc_increase > malloc_limit) {
00119         garbage_collect();
00120     }
00121     RUBY_CRITICAL(mem = malloc(size));
00122     if (!mem) {
00123         garbage_collect();
00124         RUBY_CRITICAL(mem = malloc(size));
00125         if (!mem) {
00126             rb_memerror();
00127         }
00128     }
00129 
00130     return mem;
00131 }
00132 
00133 void *
00134 ruby_xcalloc(n, size)
00135     long n, size;
00136 {
00137     void *mem;
00138 
00139     mem = xmalloc(n * size);
00140     memset(mem, 0, n * size);
00141 
00142     return mem;
00143 }
00144 
00145 void *
00146 ruby_xrealloc(ptr, size)
00147     void *ptr;
00148     long size;
00149 {
00150     void *mem;
00151 
00152     if (size < 0) {
00153         rb_raise(rb_eArgError, "negative re-allocation size");
00154     }
00155     if (!ptr) return xmalloc(size);
00156     if (size == 0) size = 1;
00157     malloc_increase += size;
00158     RUBY_CRITICAL(mem = realloc(ptr, size));
00159     if (!mem) {
00160         garbage_collect();
00161         RUBY_CRITICAL(mem = realloc(ptr, size));
00162         if (!mem) {
00163             rb_memerror();
00164         }
00165     }
00166 
00167     return mem;
00168 }
00169 
00170 void
00171 ruby_xfree(x)
00172     void *x;
00173 {
00174     if (x)
00175         RUBY_CRITICAL(free(x));
00176 }
00177 
00178 extern int ruby_in_compile;
00179 static int dont_gc;
00180 static int during_gc;
00181 static int need_call_final = 0;
00182 static st_table *finalizer_table = 0;
00183 
00184 
00185 /*
00186  *  call-seq:
00187  *     GC.enable    => true or false
00188  *
00189  *  Enables garbage collection, returning <code>true</code> if garbage
00190  *  collection was previously disabled.
00191  *
00192  *     GC.disable   #=> false
00193  *     GC.enable    #=> true
00194  *     GC.enable    #=> false
00195  *
00196  */
00197 
00198 VALUE
00199 rb_gc_enable()
00200 {
00201     int old = dont_gc;
00202 
00203     dont_gc = Qfalse;
00204     return old;
00205 }
00206 
00207 /*
00208  *  call-seq:
00209  *     GC.disable    => true or false
00210  *
00211  *  Disables garbage collection, returning <code>true</code> if garbage
00212  *  collection was already disabled.
00213  *
00214  *     GC.disable   #=> false
00215  *     GC.disable   #=> true
00216  *
00217  */
00218 
00219 VALUE
00220 rb_gc_disable()
00221 {
00222     int old = dont_gc;
00223 
00224     dont_gc = Qtrue;
00225     return old;
00226 }
00227 
00228 VALUE rb_mGC;
00229 
00230 static struct gc_list {
00231     VALUE *varptr;
00232     struct gc_list *next;
00233 } *global_List = 0;
00234 
00235 void
00236 rb_gc_register_address(addr)
00237     VALUE *addr;
00238 {
00239     struct gc_list *tmp;
00240 
00241     tmp = ALLOC(struct gc_list);
00242     tmp->next = global_List;
00243     tmp->varptr = addr;
00244     global_List = tmp;
00245 }
00246 
00247 void
00248 rb_gc_unregister_address(addr)
00249     VALUE *addr;
00250 {
00251     struct gc_list *tmp = global_List;
00252 
00253     if (tmp->varptr == addr) {
00254         global_List = tmp->next;
00255         RUBY_CRITICAL(free(tmp));
00256         return;
00257     }
00258     while (tmp->next) {
00259         if (tmp->next->varptr == addr) {
00260             struct gc_list *t = tmp->next;
00261 
00262             tmp->next = tmp->next->next;
00263             RUBY_CRITICAL(free(t));
00264             break;
00265         }
00266         tmp = tmp->next;
00267     }
00268 }
00269 
00270 #undef GC_DEBUG
00271 
00272 void
00273 rb_global_variable(var)
00274     VALUE *var;
00275 {
00276     rb_gc_register_address(var);
00277 }
00278 
00279 typedef struct RVALUE {
00280     union {
00281         struct {
00282             unsigned long flags;        /* always 0 for freed obj */
00283             struct RVALUE *next;
00284         } free;
00285         struct RBasic  basic;
00286         struct RObject object;
00287         struct RClass  klass;
00288         struct RFloat  flonum;
00289         struct RString string;
00290         struct RArray  array;
00291         struct RRegexp regexp;
00292         struct RHash   hash;
00293         struct RData   data;
00294         struct RStruct rstruct;
00295         struct RBignum bignum;
00296         struct RFile   file;
00297         struct RNode   node;
00298         struct RMatch  match;
00299         struct RVarmap varmap;
00300         struct SCOPE   scope;
00301     } as;
00302 #ifdef GC_DEBUG
00303     char *file;
00304     int   line;
00305 #endif
00306 } RVALUE;
00307 
00308 static RVALUE *freelist = 0;
00309 static RVALUE *deferred_final_list = 0;
00310 
00311 #define HEAPS_INCREMENT 10
00312 static struct heaps_slot {
00313     RVALUE *slot;
00314     int limit;
00315 } *heaps;
00316 static int heaps_length = 0;
00317 static int heaps_used   = 0;
00318 
00319 #define HEAP_MIN_SLOTS 10000
00320 static int heap_slots = HEAP_MIN_SLOTS;
00321 
00322 #define FREE_MIN  4096
00323 
00324 static RVALUE *himem, *lomem;
00325 
00326 static void
00327 add_heap()
00328 {
00329     RVALUE *p, *pend;
00330 
00331     if (heaps_used == heaps_length) {
00332         /* Realloc heaps */
00333         struct heaps_slot *p;
00334         int length;
00335 
00336         heaps_length += HEAPS_INCREMENT;
00337         length = heaps_length*sizeof(struct heaps_slot);
00338         RUBY_CRITICAL(
00339             if (heaps_used > 0) {
00340                 p = (struct heaps_slot *)realloc(heaps, length);
00341                 if (p) heaps = p;
00342             }
00343             else {
00344                 p = heaps = (struct heaps_slot *)malloc(length);
00345             });
00346         if (p == 0) rb_memerror();
00347     }
00348 
00349     for (;;) {
00350         RUBY_CRITICAL(p = heaps[heaps_used].slot = (RVALUE*)malloc(sizeof(RVALUE)*heap_slots));
00351         heaps[heaps_used].limit = heap_slots;
00352         if (p == 0) {
00353             if (heap_slots == HEAP_MIN_SLOTS) {
00354                 rb_memerror();
00355             }
00356             heap_slots = HEAP_MIN_SLOTS;
00357             continue;
00358         }
00359         break;
00360     }
00361     pend = p + heap_slots;
00362     if (lomem == 0 || lomem > p) lomem = p;
00363     if (himem < pend) himem = pend;
00364     heaps_used++;
00365     heap_slots *= 1.8;
00366 
00367     while (p < pend) {
00368         p->as.free.flags = 0;
00369         p->as.free.next = freelist;
00370         freelist = p;
00371         p++;
00372     }
00373 }
00374 #define RANY(o) ((RVALUE*)(o))
00375 
00376 VALUE
00377 rb_newobj()
00378 {
00379     VALUE obj;
00380 
00381     if (!freelist) garbage_collect();
00382 
00383     obj = (VALUE)freelist;
00384     freelist = freelist->as.free.next;
00385     MEMZERO((void*)obj, RVALUE, 1);
00386 #ifdef GC_DEBUG
00387     RANY(obj)->file = ruby_sourcefile;
00388     RANY(obj)->line = ruby_sourceline;
00389 #endif
00390     return obj;
00391 }
00392 
00393 VALUE
00394 rb_data_object_alloc(klass, datap, dmark, dfree)
00395     VALUE klass;
00396     void *datap;
00397     RUBY_DATA_FUNC dmark;
00398     RUBY_DATA_FUNC dfree;
00399 {
00400     NEWOBJ(data, struct RData);
00401     if (klass) Check_Type(klass, T_CLASS);
00402     OBJSETUP(data, klass, T_DATA);
00403     data->data = datap;
00404     data->dfree = dfree;
00405     data->dmark = dmark;
00406 
00407     return (VALUE)data;
00408 }
00409 
00410 extern st_table *rb_class_tbl;
00411 VALUE *rb_gc_stack_start = 0;
00412 
00413 #ifdef DJGPP
00414 /* set stack size (http://www.delorie.com/djgpp/v2faq/faq15_9.html) */
00415 unsigned int _stklen = 0x180000; /* 1.5 kB */
00416 #endif
00417 
00418 #if defined(DJGPP) || defined(_WIN32_WCE)
00419 static unsigned int STACK_LEVEL_MAX = 65535;
00420 #elif defined(__human68k__)
00421 unsigned int _stacksize = 262144;
00422 # define STACK_LEVEL_MAX (_stacksize - 4096)
00423 # undef HAVE_GETRLIMIT
00424 #elif defined(HAVE_GETRLIMIT)
00425 static unsigned int STACK_LEVEL_MAX = 655300;
00426 #else
00427 # define STACK_LEVEL_MAX 655300
00428 #endif
00429 
00430 #ifdef C_ALLOCA
00431 # define SET_STACK_END VALUE stack_end; alloca(0);
00432 # define STACK_END (&stack_end)
00433 #else
00434 # if defined(__GNUC__) && defined(USE_BUILTIN_FRAME_ADDRESS) && !defined(__ia64__)
00435 #  if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3
00436 __attribute__ ((noinline))
00437 #  endif
00438 static VALUE *
00439 stack_end_address(void)
00440 {
00441     return (VALUE *)__builtin_frame_address(0);
00442 }
00443 #  define  SET_STACK_END    VALUE *stack_end = stack_end_address()
00444 # else
00445 #  define  SET_STACK_END    VALUE *stack_end = alloca(1)
00446 # endif
00447 # define STACK_END (stack_end)
00448 #endif
00449 #if defined(sparc) || defined(__sparc__)
00450 # define STACK_LENGTH  (rb_gc_stack_start - STACK_END + 0x80)
00451 #elif STACK_GROW_DIRECTION < 0
00452 # define STACK_LENGTH  (rb_gc_stack_start - STACK_END)
00453 #elif STACK_GROW_DIRECTION > 0
00454 # define STACK_LENGTH  (STACK_END - rb_gc_stack_start + 1)
00455 #else
00456 # define STACK_LENGTH  ((STACK_END < rb_gc_stack_start) ? rb_gc_stack_start - STACK_END\
00457                                            : STACK_END - rb_gc_stack_start + 1)
00458 #endif
00459 #if STACK_GROW_DIRECTION > 0
00460 # define STACK_UPPER(x, a, b) a
00461 #elif STACK_GROW_DIRECTION < 0
00462 # define STACK_UPPER(x, a, b) b
00463 #else
00464 static int grow_direction;
00465 static int
00466 stack_grow_direction(addr)
00467     VALUE *addr;
00468 {
00469     SET_STACK_END;
00470 
00471     if (STACK_END > addr) return grow_direction = 1;
00472     return grow_direction = -1;
00473 }
00474 # define stack_growup_p(x) ((grow_direction ? grow_direction : stack_grow_direction(x)) > 0)
00475 # define STACK_UPPER(x, a, b) (stack_growup_p(x) ? a : b)
00476 #endif
00477 
00478 #define GC_WATER_MARK 512
00479 
00480 #define CHECK_STACK(ret) do {\
00481     SET_STACK_END;\
00482     (ret) = (STACK_LENGTH > STACK_LEVEL_MAX + GC_WATER_MARK);\
00483 } while (0)
00484 
00485 int
00486 ruby_stack_length(p)
00487     VALUE **p;
00488 {
00489     SET_STACK_END;
00490     if (p) *p = STACK_UPPER(STACK_END, rb_gc_stack_start, STACK_END);
00491     return STACK_LENGTH;
00492 }
00493 
00494 int
00495 ruby_stack_check()
00496 {
00497     int ret;
00498 
00499     CHECK_STACK(ret);
00500     return ret;
00501 }
00502 
00503 #define MARK_STACK_MAX 1024
00504 static VALUE mark_stack[MARK_STACK_MAX];
00505 static VALUE *mark_stack_ptr;
00506 static int mark_stack_overflow;
00507 
00508 static void
00509 init_mark_stack()
00510 {
00511     mark_stack_overflow = 0;
00512     mark_stack_ptr = mark_stack;
00513 }
00514 
00515 #define MARK_STACK_EMPTY (mark_stack_ptr == mark_stack)
00516             
00517 static st_table *source_filenames;
00518 
00519 char *
00520 rb_source_filename(f)
00521     const char *f;
00522 {
00523     char *name;
00524 
00525     if (!st_lookup(source_filenames, (st_data_t)f, (st_data_t *)&name)) {
00526         long len = strlen(f) + 1;
00527         char *ptr = name = ALLOC_N(char, len + 1);
00528         *ptr++ = 0;
00529         MEMCPY(ptr, f, char, len);
00530         st_add_direct(source_filenames, (st_data_t)ptr, (st_data_t)name);
00531         return ptr;
00532     }
00533     return name + 1;
00534 }
00535 
00536 static void
00537 mark_source_filename(f)
00538     char *f;
00539 {
00540     if (f) {
00541         f[-1] = 1;
00542     }
00543 }
00544 
00545 static int
00546 sweep_source_filename(key, value)
00547     char *key, *value;
00548 {
00549     if (*value) {
00550         *value = 0;
00551         return ST_CONTINUE;
00552     }
00553     else {
00554         free(value);
00555         return ST_DELETE;
00556     }
00557 }
00558 
00559 static void gc_mark (VALUE ptr, int lev);
00560 static void gc_mark_children (VALUE ptr, int lev);
00561 
00562 static void
00563 gc_mark_all()
00564 {
00565     RVALUE *p, *pend;
00566     int i;
00567 
00568     init_mark_stack();
00569     for (i = 0; i < heaps_used; i++) {
00570         p = heaps[i].slot; pend = p + heaps[i].limit;
00571         while (p < pend) {
00572             if ((p->as.basic.flags & FL_MARK) &&
00573                 (p->as.basic.flags != FL_MARK)) {
00574                 gc_mark_children((VALUE)p, 0);
00575             }
00576             p++;
00577         }
00578     }
00579 }
00580 
00581 static void
00582 gc_mark_rest()
00583 {
00584     VALUE tmp_arry[MARK_STACK_MAX];
00585     VALUE *p;
00586 
00587     p = (mark_stack_ptr - mark_stack) + tmp_arry;
00588     MEMCPY(tmp_arry, mark_stack, VALUE, MARK_STACK_MAX);
00589 
00590     init_mark_stack();
00591     while(p != tmp_arry){
00592         p--;
00593         gc_mark_children(*p, 0);
00594     }
00595 }
00596 
00597 static inline int
00598 is_pointer_to_heap(ptr)
00599     void *ptr;
00600 {
00601     register RVALUE *p = RANY(ptr);
00602     register RVALUE *heap_org;
00603     register long i;
00604 
00605     if (p < lomem || p > himem) return Qfalse;
00606 
00607     /* check if p looks like a pointer */
00608     for (i=0; i < heaps_used; i++) {
00609         heap_org = heaps[i].slot;
00610         if (heap_org <= p && p < heap_org + heaps[i].limit &&
00611             ((((char*)p)-((char*)heap_org))%sizeof(RVALUE)) == 0)
00612             return Qtrue;
00613     }
00614     return Qfalse;
00615 }
00616 
00617 static void
00618 mark_locations_array(x, n)
00619     register VALUE *x;
00620     register long n;
00621 {
00622     VALUE v;
00623     while (n--) {
00624         v = *x;
00625         if (is_pointer_to_heap((void *)v)) {
00626             gc_mark(v, 0);
00627         }
00628         x++;
00629     }
00630 }
00631 
00632 void
00633 rb_gc_mark_locations(start, end)
00634     VALUE *start, *end;
00635 {
00636     long n;
00637 
00638     n = end - start;
00639     mark_locations_array(start,n);
00640 }
00641 
00642 static int
00643 mark_entry(key, value, lev)
00644     ID key;
00645     VALUE value;
00646     int lev;
00647 {
00648     gc_mark(value, lev);
00649     return ST_CONTINUE;
00650 }
00651 
00652 static void
00653 mark_tbl(tbl, lev)
00654     st_table *tbl;
00655     int lev;
00656 {
00657     if (!tbl) return;
00658     st_foreach(tbl, mark_entry, lev);
00659 }
00660 
00661 void
00662 rb_mark_tbl(tbl)
00663     st_table *tbl;
00664 {
00665     mark_tbl(tbl, 0);
00666 }
00667 
00668 static int
00669 mark_keyvalue(key, value, lev)
00670     VALUE key;
00671     VALUE value;
00672     int lev;
00673 {
00674     gc_mark(key, lev);
00675     gc_mark(value, lev);
00676     return ST_CONTINUE;
00677 }
00678 
00679 static void
00680 mark_hash(tbl, lev)
00681     st_table *tbl;
00682     int lev;
00683 {
00684     if (!tbl) return;
00685     st_foreach(tbl, mark_keyvalue, lev);
00686 }
00687 
00688 void
00689 rb_mark_hash(tbl)
00690     st_table *tbl;
00691 {
00692     mark_hash(tbl, 0);
00693 }
00694 
00695 void
00696 rb_gc_mark_maybe(obj)
00697     VALUE obj;
00698 {
00699     if (is_pointer_to_heap((void *)obj)) {
00700         gc_mark(obj, 0);
00701     }
00702 }
00703 
00704 #define GC_LEVEL_MAX 250
00705 
00706 static void
00707 gc_mark(ptr, lev)
00708     VALUE ptr;
00709     int lev;
00710 {
00711     register RVALUE *obj;
00712 
00713     obj = RANY(ptr);
00714     if (rb_special_const_p(ptr)) return; /* special const not marked */
00715     if (obj->as.basic.flags == 0) return;       /* free cell */
00716     if (obj->as.basic.flags & FL_MARK) return;  /* already marked */
00717     obj->as.basic.flags |= FL_MARK;
00718 
00719     if (lev > GC_LEVEL_MAX || (lev == 0 && ruby_stack_check())) {
00720         if (!mark_stack_overflow) {
00721             if (mark_stack_ptr - mark_stack < MARK_STACK_MAX) {
00722                 *mark_stack_ptr = ptr;
00723                 mark_stack_ptr++;               
00724             }
00725             else {
00726                 mark_stack_overflow = 1;
00727             }
00728         }
00729         return;
00730     }
00731     gc_mark_children(ptr, lev+1);
00732 }
00733 
00734 void
00735 rb_gc_mark(ptr)
00736     VALUE ptr;
00737 {
00738     gc_mark(ptr, 0);
00739 }
00740 
00741 static void
00742 gc_mark_children(ptr, lev)
00743     VALUE ptr;
00744     int lev;
00745 {
00746     register RVALUE *obj = RANY(ptr);
00747 
00748     goto marking;               /* skip */
00749 
00750   again:
00751     obj = RANY(ptr);
00752     if (rb_special_const_p(ptr)) return; /* special const not marked */
00753     if (obj->as.basic.flags == 0) return;       /* free cell */
00754     if (obj->as.basic.flags & FL_MARK) return;  /* already marked */
00755     obj->as.basic.flags |= FL_MARK;
00756 
00757   marking:
00758     if (FL_TEST(obj, FL_EXIVAR)) {
00759         rb_mark_generic_ivar(ptr);
00760     }
00761 
00762     switch (obj->as.basic.flags & T_MASK) {
00763       case T_NIL:
00764       case T_FIXNUM:
00765         rb_bug("rb_gc_mark() called for broken object");
00766         break;
00767 
00768       case T_NODE:
00769         mark_source_filename(obj->as.node.nd_file);
00770         switch (nd_type(obj)) {
00771           case NODE_IF:         /* 1,2,3 */
00772           case NODE_FOR:
00773           case NODE_ITER:
00774           case NODE_CREF:
00775           case NODE_WHEN:
00776           case NODE_MASGN:
00777           case NODE_RESCUE:
00778           case NODE_RESBODY:
00779           case NODE_CLASS:
00780             gc_mark((VALUE)obj->as.node.u2.node, lev);
00781             /* fall through */
00782           case NODE_BLOCK:      /* 1,3 */
00783           case NODE_ARRAY:
00784           case NODE_DSTR:
00785           case NODE_DXSTR:
00786           case NODE_DREGX:
00787           case NODE_DREGX_ONCE:
00788           case NODE_FBODY:
00789           case NODE_ENSURE:
00790           case NODE_CALL:
00791           case NODE_DEFS:
00792           case NODE_OP_ASGN1:
00793             gc_mark((VALUE)obj->as.node.u1.node, lev);
00794             /* fall through */
00795           case NODE_SUPER:      /* 3 */
00796           case NODE_FCALL:
00797           case NODE_DEFN:
00798           case NODE_NEWLINE:
00799             ptr = (VALUE)obj->as.node.u3.node;
00800             goto again;
00801 
00802           case NODE_WHILE:      /* 1,2 */
00803           case NODE_UNTIL:
00804           case NODE_AND:
00805           case NODE_OR:
00806           case NODE_CASE:
00807           case NODE_SCLASS:
00808           case NODE_DOT2:
00809           case NODE_DOT3:
00810           case NODE_FLIP2:
00811           case NODE_FLIP3:
00812           case NODE_MATCH2:
00813           case NODE_MATCH3:
00814           case NODE_OP_ASGN_OR:
00815           case NODE_OP_ASGN_AND:
00816           case NODE_MODULE:
00817             gc_mark((VALUE)obj->as.node.u1.node, lev);
00818             /* fall through */
00819           case NODE_METHOD:     /* 2 */
00820           case NODE_NOT:
00821           case NODE_GASGN:
00822           case NODE_LASGN:
00823           case NODE_DASGN:
00824           case NODE_DASGN_CURR:
00825           case NODE_IASGN:
00826           case NODE_CVDECL:
00827           case NODE_CVASGN:
00828           case NODE_COLON3:
00829           case NODE_OPT_N:
00830           case NODE_EVSTR:
00831             ptr = (VALUE)obj->as.node.u2.node;
00832             goto again;
00833 
00834           case NODE_HASH:       /* 1 */
00835           case NODE_LIT:
00836           case NODE_STR:
00837           case NODE_XSTR:
00838           case NODE_DEFINED:
00839           case NODE_MATCH:
00840           case NODE_RETURN:
00841           case NODE_BREAK:
00842           case NODE_NEXT:
00843           case NODE_YIELD:
00844           case NODE_COLON2:
00845           case NODE_ARGS:
00846           case NODE_SPLAT:
00847           case NODE_TO_ARY:
00848           case NODE_SVALUE:
00849             ptr = (VALUE)obj->as.node.u1.node;
00850             goto again;
00851 
00852           case NODE_SCOPE:      /* 2,3 */
00853           case NODE_BLOCK_PASS:
00854           case NODE_CDECL:
00855             gc_mark((VALUE)obj->as.node.u3.node, lev);
00856             ptr = (VALUE)obj->as.node.u2.node;
00857             goto again;
00858 
00859           case NODE_ZARRAY:     /* - */
00860           case NODE_ZSUPER:
00861           case NODE_CFUNC:
00862           case NODE_VCALL:
00863           case NODE_GVAR:
00864           case NODE_LVAR:
00865           case NODE_DVAR:
00866           case NODE_IVAR:
00867           case NODE_CVAR:
00868           case NODE_NTH_REF:
00869           case NODE_BACK_REF:
00870           case NODE_ALIAS:
00871           case NODE_VALIAS:
00872           case NODE_REDO:
00873           case NODE_RETRY:
00874           case NODE_UNDEF:
00875           case NODE_SELF:
00876           case NODE_NIL:
00877           case NODE_TRUE:
00878           case NODE_FALSE:
00879           case NODE_ATTRSET:
00880           case NODE_BLOCK_ARG:
00881           case NODE_POSTEXE:
00882             break;
00883           case NODE_ALLOCA:
00884             mark_locations_array((VALUE*)obj->as.node.u1.value,
00885                                  obj->as.node.u3.cnt);
00886             ptr = (VALUE)obj->as.node.u2.node;
00887             goto again;
00888 
00889           default:              /* unlisted NODE */
00890             if (is_pointer_to_heap(obj->as.node.u1.node)) {
00891                 gc_mark((VALUE)obj->as.node.u1.node, lev);
00892             }
00893             if (is_pointer_to_heap(obj->as.node.u2.node)) {
00894                 gc_mark((VALUE)obj->as.node.u2.node, lev);
00895             }
00896             if (is_pointer_to_heap(obj->as.node.u3.node)) {
00897                 gc_mark((VALUE)obj->as.node.u3.node, lev);
00898             }
00899         }
00900         return;                 /* no need to mark class. */
00901     }
00902 
00903     gc_mark(obj->as.basic.klass, lev);
00904     switch (obj->as.basic.flags & T_MASK) {
00905       case T_ICLASS:
00906       case T_CLASS:
00907       case T_MODULE:
00908         mark_tbl(obj->as.klass.m_tbl, lev);
00909         mark_tbl(obj->as.klass.iv_tbl, lev);
00910         ptr = obj->as.klass.super;
00911         goto again;
00912 
00913       case T_ARRAY:
00914         if (FL_TEST(obj, ELTS_SHARED)) {
00915             ptr = obj->as.array.aux.shared;
00916             goto again;
00917         }
00918         else {
00919             long i, len = obj->as.array.len;
00920             VALUE *ptr = obj->as.array.ptr;
00921 
00922             for (i=0; i < len; i++) {
00923                 gc_mark(*ptr++, lev);
00924             }
00925         }
00926         break;
00927 
00928       case T_HASH:
00929         mark_hash(obj->as.hash.tbl, lev);
00930         ptr = obj->as.hash.ifnone;
00931         goto again;
00932 
00933       case T_STRING:
00934 #define STR_ASSOC FL_USER3   /* copied from string.c */
00935         if (FL_TEST(obj, ELTS_SHARED|STR_ASSOC)) {
00936             ptr = obj->as.string.aux.shared;
00937             goto again;
00938         }
00939         break;
00940 
00941       case T_DATA:
00942         if (obj->as.data.dmark) (*obj->as.data.dmark)(DATA_PTR(obj));
00943         break;
00944 
00945       case T_OBJECT:
00946         mark_tbl(obj->as.object.iv_tbl, lev);
00947         break;
00948 
00949       case T_FILE:
00950       case T_REGEXP:
00951       case T_FLOAT:
00952       case T_BIGNUM:
00953       case T_BLKTAG:
00954         break;
00955 
00956       case T_MATCH:
00957         if (obj->as.match.str) {
00958             ptr = obj->as.match.str;
00959             goto again;
00960         }
00961         break;
00962 
00963       case T_VARMAP:
00964         gc_mark(obj->as.varmap.val, lev);
00965         ptr = (VALUE)obj->as.varmap.next;
00966         goto again;
00967 
00968       case T_SCOPE:
00969         if (obj->as.scope.local_vars && (obj->as.scope.flags & SCOPE_MALLOC)) {
00970             int n = obj->as.scope.local_tbl[0]+1;
00971             VALUE *vars = &obj->as.scope.local_vars[-1];
00972 
00973             while (n--) {
00974                 gc_mark(*vars++, lev);
00975             }
00976         }
00977         break;
00978 
00979       case T_STRUCT:
00980         {
00981             long len = obj->as.rstruct.len;
00982             VALUE *ptr = obj->as.rstruct.ptr;
00983 
00984             while (len--) {
00985                 gc_mark(*ptr++, lev);
00986             }
00987         }
00988         break;
00989 
00990       default:
00991         rb_bug("rb_gc_mark(): unknown data type 0x%lx(0x%lx) %s",
00992                obj->as.basic.flags & T_MASK, obj,
00993                is_pointer_to_heap(obj) ? "corrupted object" : "non object");
00994     }
00995 }
00996 
00997 static void obj_free (VALUE);
00998 
00999 static void
01000 finalize_list(p)
01001     RVALUE *p;
01002 {
01003     while (p) {
01004         RVALUE *tmp = p->as.free.next;
01005         run_final((VALUE)p);
01006         if (!FL_TEST(p, FL_SINGLETON)) { /* not freeing page */
01007             p->as.free.flags = 0;
01008             p->as.free.next = freelist;
01009             freelist = p;
01010         }
01011         p = tmp;
01012     }
01013 }
01014 
01015 static void
01016 free_unused_heaps()
01017 {
01018     int i, j;
01019 
01020     for (i = j = 1; j < heaps_used; i++) {
01021         if (heaps[i].limit == 0) {
01022             free(heaps[i].slot);
01023             heaps_used--;
01024         }
01025         else {
01026             if (i != j) {
01027                 heaps[j] = heaps[i];
01028             }
01029             j++;
01030         }
01031     }
01032 }
01033 
01034 static void
01035 gc_sweep()
01036 {
01037     RVALUE *p, *pend, *final_list;
01038     int freed = 0;
01039     int i;
01040     unsigned long live = 0;
01041 
01042     if (ruby_in_compile && ruby_parser_stack_on_heap()) {
01043         /* should not reclaim nodes during compilation
01044            if yacc's semantic stack is not allocated on machine stack */
01045         for (i = 0; i < heaps_used; i++) {
01046             p = heaps[i].slot; pend = p + heaps[i].limit;
01047             while (p < pend) {
01048                 if (!(p->as.basic.flags&FL_MARK) && BUILTIN_TYPE(p) == T_NODE)
01049                     gc_mark((VALUE)p, 0);
01050                 p++;
01051             }
01052         }
01053     }
01054 
01055     mark_source_filename(ruby_sourcefile);
01056     if (source_filenames) {
01057         st_foreach(source_filenames, sweep_source_filename, 0);
01058     }
01059 
01060     freelist = 0;
01061     final_list = deferred_final_list;
01062     deferred_final_list = 0;
01063     for (i = 0; i < heaps_used; i++) {
01064         int n = 0;
01065         RVALUE *free = freelist;
01066         RVALUE *final = final_list;
01067 
01068         p = heaps[i].slot; pend = p + heaps[i].limit;
01069         while (p < pend) {
01070             if (!(p->as.basic.flags & FL_MARK)) {
01071                 if (p->as.basic.flags) {
01072                     obj_free((VALUE)p);
01073                 }
01074                 if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
01075                     p->as.free.flags = FL_MARK; /* remain marked */
01076                     p->as.free.next = final_list;
01077                     final_list = p;
01078                 }
01079                 else {
01080                     p->as.free.flags = 0;
01081                     p->as.free.next = freelist;
01082                     freelist = p;
01083                 }
01084                 n++;
01085             }
01086             else if (RBASIC(p)->flags == FL_MARK) {
01087                 /* objects to be finalized */
01088                 /* do notning remain marked */
01089             }
01090             else {
01091                 RBASIC(p)->flags &= ~FL_MARK;
01092                 live++;
01093             }
01094             p++;
01095         }
01096         if (n == heaps[i].limit && freed > FREE_MIN) {
01097             RVALUE *pp;
01098 
01099             heaps[i].limit = 0;
01100             for (pp = final_list; pp != final; pp = pp->as.free.next) {
01101                 p->as.free.flags |= FL_SINGLETON; /* freeing page mark */
01102             }
01103             freelist = free;    /* cancel this page from freelist */
01104         }
01105         else {
01106             freed += n;
01107         }
01108     }
01109     if (malloc_increase > malloc_limit) {
01110         malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
01111         if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
01112     }
01113     malloc_increase = 0;
01114     if (freed < FREE_MIN) {
01115         add_heap();
01116     }
01117     during_gc = 0;
01118 
01119     /* clear finalization list */
01120     if (final_list) {
01121         deferred_final_list = final_list;
01122         return;
01123     }
01124     free_unused_heaps();
01125 }
01126 
01127 void
01128 rb_gc_force_recycle(p)
01129     VALUE p;
01130 {
01131     RANY(p)->as.free.flags = 0;
01132     RANY(p)->as.free.next = freelist;
01133     freelist = RANY(p);
01134 }
01135 
01136 static void
01137 obj_free(obj)
01138     VALUE obj;
01139 {
01140     switch (RANY(obj)->as.basic.flags & T_MASK) {
01141       case T_NIL:
01142       case T_FIXNUM:
01143       case T_TRUE:
01144       case T_FALSE:
01145         rb_bug("obj_free() called for broken object");
01146         break;
01147     }
01148 
01149     if (FL_TEST(obj, FL_EXIVAR)) {
01150         rb_free_generic_ivar((VALUE)obj);
01151     }
01152 
01153     switch (RANY(obj)->as.basic.flags & T_MASK) {
01154       case T_OBJECT:
01155         if (RANY(obj)->as.object.iv_tbl) {
01156             st_free_table(RANY(obj)->as.object.iv_tbl);
01157         }
01158         break;
01159       case T_MODULE:
01160       case T_CLASS:
01161         rb_clear_cache_by_class((VALUE)obj);
01162         st_free_table(RANY(obj)->as.klass.m_tbl);
01163         if (RANY(obj)->as.object.iv_tbl) {
01164             st_free_table(RANY(obj)->as.object.iv_tbl);
01165         }
01166         break;
01167       case T_STRING:
01168         if (RANY(obj)->as.string.ptr && !FL_TEST(obj, ELTS_SHARED)) {
01169             RUBY_CRITICAL(free(RANY(obj)->as.string.ptr));
01170         }
01171         break;
01172       case T_ARRAY:
01173         if (RANY(obj)->as.array.ptr && !FL_TEST(obj, ELTS_SHARED)) {
01174             RUBY_CRITICAL(free(RANY(obj)->as.array.ptr));
01175         }
01176         break;
01177       case T_HASH:
01178         if (RANY(obj)->as.hash.tbl) {
01179             st_free_table(RANY(obj)->as.hash.tbl);
01180         }
01181         break;
01182       case T_REGEXP:
01183         if (RANY(obj)->as.regexp.ptr) {
01184             re_free_pattern(RANY(obj)->as.regexp.ptr);
01185         }
01186         if (RANY(obj)->as.regexp.str) {
01187             RUBY_CRITICAL(free(RANY(obj)->as.regexp.str));
01188         }
01189         break;
01190       case T_DATA:
01191         if (DATA_PTR(obj)) {
01192             if ((long)RANY(obj)->as.data.dfree == -1) {
01193                 RUBY_CRITICAL(free(DATA_PTR(obj)));
01194             }
01195             else if (RANY(obj)->as.data.dfree) {
01196                 (*RANY(obj)->as.data.dfree)(DATA_PTR(obj));
01197             }
01198         }
01199         break;
01200       case T_MATCH:
01201         if (RANY(obj)->as.match.regs) {
01202             re_free_registers(RANY(obj)->as.match.regs);
01203             RUBY_CRITICAL(free(RANY(obj)->as.match.regs));
01204         }
01205         break;
01206       case T_FILE:
01207         if (RANY(obj)->as.file.fptr) {
01208             rb_io_fptr_finalize(RANY(obj)->as.file.fptr);
01209             RUBY_CRITICAL(free(RANY(obj)->as.file.fptr));
01210         }
01211         break;
01212       case T_ICLASS:
01213         /* iClass shares table with the module */
01214         break;
01215 
01216       case T_FLOAT:
01217       case T_VARMAP:
01218       case T_BLKTAG:
01219         break;
01220 
01221       case T_BIGNUM:
01222         if (RANY(obj)->as.bignum.digits) {
01223             RUBY_CRITICAL(free(RANY(obj)->as.bignum.digits));
01224         }
01225         break;
01226       case T_NODE:
01227         switch (nd_type(obj)) {
01228           case NODE_SCOPE:
01229             if (RANY(obj)->as.node.u1.tbl) {
01230                 RUBY_CRITICAL(free(RANY(obj)->as.node.u1.tbl));
01231             }
01232             break;
01233           case NODE_ALLOCA:
01234             RUBY_CRITICAL(free(RANY(obj)->as.node.u1.node));
01235             break;
01236         }
01237         return;                 /* no need to free iv_tbl */
01238 
01239       case T_SCOPE:
01240         if (RANY(obj)->as.scope.local_vars &&
01241             RANY(obj)->as.scope.flags != SCOPE_ALLOCA) {
01242             VALUE *vars = RANY(obj)->as.scope.local_vars-1;
01243             if (vars[0] == 0)
01244                 RUBY_CRITICAL(free(RANY(obj)->as.scope.local_tbl));
01245             if (RANY(obj)->as.scope.flags & SCOPE_MALLOC)
01246                 RUBY_CRITICAL(free(vars));
01247         }
01248         break;
01249 
01250       case T_STRUCT:
01251         if (RANY(obj)->as.rstruct.ptr) {
01252             RUBY_CRITICAL(free(RANY(obj)->as.rstruct.ptr));
01253         }
01254         break;
01255 
01256       default:
01257         rb_bug("gc_sweep(): unknown data type 0x%lx(0x%lx)",
01258                RANY(obj)->as.basic.flags & T_MASK, obj);
01259     }
01260 }
01261 
01262 void
01263 rb_gc_mark_frame(frame)
01264     struct FRAME *frame;
01265 {
01266     mark_locations_array(frame->argv, frame->argc);
01267     gc_mark((VALUE)frame->node, 0);
01268 }
01269 
01270 #ifdef __GNUC__
01271 #if defined(__human68k__) || defined(DJGPP)
01272 #if defined(__human68k__)
01273 typedef unsigned long rb_jmp_buf[8];
01274 __asm__ (".even\n\
01275 _rb_setjmp:\n\
01276         move.l  4(sp),a0\n\
01277         movem.l d3-d7/a3-a5,(a0)\n\
01278         moveq.l #0,d0\n\
01279         rts");
01280 #ifdef setjmp
01281 #undef setjmp
01282 #endif
01283 #else
01284 #if defined(DJGPP)
01285 typedef unsigned long rb_jmp_buf[6];
01286 __asm__ (".align 4\n\
01287 _rb_setjmp:\n\
01288         pushl   %ebp\n\
01289         movl    %esp,%ebp\n\
01290         movl    8(%ebp),%ebp\n\
01291         movl    %eax,(%ebp)\n\
01292         movl    %ebx,4(%ebp)\n\
01293         movl    %ecx,8(%ebp)\n\
01294         movl    %edx,12(%ebp)\n\
01295         movl    %esi,16(%ebp)\n\
01296         movl    %edi,20(%ebp)\n\
01297         popl    %ebp\n\
01298         xorl    %eax,%eax\n\
01299         ret");
01300 #endif
01301 #endif
01302 int rb_setjmp (rb_jmp_buf);
01303 #define jmp_buf rb_jmp_buf
01304 #define setjmp rb_setjmp
01305 #endif /* __human68k__ or DJGPP */
01306 #endif /* __GNUC__ */
01307 
01308 static void
01309 garbage_collect()
01310 {
01311     struct gc_list *list;
01312     struct FRAME * volatile frame; /* gcc 2.7.2.3 -O2 bug??  */
01313     jmp_buf save_regs_gc_mark;
01314     SET_STACK_END;
01315 
01316 #ifdef HAVE_NATIVETHREAD
01317     if (!is_ruby_native_thread()) {
01318         rb_bug("cross-thread violation on rb_gc()");
01319     }
01320 #endif
01321     if (dont_gc || during_gc) {
01322         if (!freelist) {
01323             add_heap();
01324         }
01325         return;
01326     }
01327     if (during_gc) return;
01328     during_gc++;
01329 
01330     init_mark_stack();
01331 
01332     /* mark frame stack */
01333     for (frame = ruby_frame; frame; frame = frame->prev) {
01334         rb_gc_mark_frame(frame);
01335         if (frame->tmp) {
01336             struct FRAME *tmp = frame->tmp;
01337             while (tmp) {
01338                 rb_gc_mark_frame(tmp);
01339                 tmp = tmp->prev;
01340             }
01341         }
01342     }
01343     gc_mark((VALUE)ruby_scope, 0);
01344     gc_mark((VALUE)ruby_dyna_vars, 0);
01345     if (finalizer_table) {
01346         mark_tbl(finalizer_table, 0);
01347     }
01348 
01349     FLUSH_REGISTER_WINDOWS;
01350     /* This assumes that all registers are saved into the jmp_buf (and stack) */
01351     setjmp(save_regs_gc_mark);
01352     mark_locations_array((VALUE*)save_regs_gc_mark, sizeof(save_regs_gc_mark) / sizeof(VALUE *));
01353 #if STACK_GROW_DIRECTION < 0
01354     rb_gc_mark_locations((VALUE*)STACK_END, rb_gc_stack_start);
01355 #elif STACK_GROW_DIRECTION > 0
01356     rb_gc_mark_locations(rb_gc_stack_start, (VALUE*)STACK_END + 1);
01357 #else
01358     if ((VALUE*)STACK_END < rb_gc_stack_start)
01359         rb_gc_mark_locations((VALUE*)STACK_END, rb_gc_stack_start);
01360     else
01361         rb_gc_mark_locations(rb_gc_stack_start, (VALUE*)STACK_END + 1);
01362 #endif
01363 #ifdef __ia64__
01364     /* mark backing store (flushed register window on the stack) */
01365     /* the basic idea from guile GC code                         */
01366     {
01367         ucontext_t ctx;
01368         VALUE *top, *bot;
01369         getcontext(&ctx);
01370         mark_locations_array((VALUE*)&ctx.uc_mcontext,
01371                              ((size_t)(sizeof(VALUE)-1 + sizeof ctx.uc_mcontext)/sizeof(VALUE)));
01372         bot = (VALUE*)__libc_ia64_register_backing_store_base;
01373 #if defined(__FreeBSD__)
01374         top = (VALUE*)ctx.uc_mcontext.mc_special.bspstore;
01375 #else
01376         top = (VALUE*)ctx.uc_mcontext.sc_ar_bsp;
01377 #endif
01378         rb_gc_mark_locations(bot, top);
01379     }
01380 #endif
01381 #if defined(__human68k__) || defined(__mc68000__)
01382     rb_gc_mark_locations((VALUE*)((char*)STACK_END + 2),
01383                          (VALUE*)((char*)rb_gc_stack_start + 2));
01384 #endif
01385     rb_gc_mark_threads();
01386 
01387     /* mark protected global variables */
01388     for (list = global_List; list; list = list->next) {
01389         rb_gc_mark_maybe(*list->varptr);
01390     }
01391     rb_mark_end_proc();
01392     rb_gc_mark_global_tbl();
01393 
01394     rb_mark_tbl(rb_class_tbl);
01395     rb_gc_mark_trap_list();
01396 
01397     /* mark generic instance variables for special constants */
01398     rb_mark_generic_ivar_tbl();
01399 
01400     rb_gc_mark_parser();
01401 
01402     /* gc_mark objects whose marking are not completed*/
01403     while (!MARK_STACK_EMPTY){
01404         if (mark_stack_overflow){
01405             gc_mark_all();
01406         }
01407         else {
01408             gc_mark_rest();
01409         }
01410     }
01411     gc_sweep();
01412 }
01413 
01414 void
01415 rb_gc()
01416 {
01417     garbage_collect();
01418     rb_gc_finalize_deferred();
01419 }
01420 
01421 /*
01422  *  call-seq:
01423  *     GC.start                     => nil
01424  *     gc.garbage_collect           => nil
01425  *     ObjectSpace.garbage_collect  => nil
01426  *
01427  *  Initiates garbage collection, unless manually disabled.
01428  *
01429  */
01430 
01431 VALUE
01432 rb_gc_start()
01433 {
01434     rb_gc();
01435     return Qnil;
01436 }
01437 
01438 void
01439 ruby_set_stack_size(size)
01440     size_t size;
01441 {
01442 #ifndef STACK_LEVEL_MAX
01443     STACK_LEVEL_MAX = size / sizeof(VALUE);
01444 #endif
01445 }
01446 
01447 void
01448 Init_stack(addr)
01449     VALUE *addr;
01450 {
01451 #if defined(_WIN32) || defined(__CYGWIN__)
01452     MEMORY_BASIC_INFORMATION m;
01453     memset(&m, 0, sizeof(m));
01454     VirtualQuery(&m, &m, sizeof(m));
01455     rb_gc_stack_start =
01456         STACK_UPPER((VALUE *)&m, (VALUE *)m.BaseAddress,
01457                     (VALUE *)((char *)m.BaseAddress + m.RegionSize) - 1);
01458 #elif defined(STACK_END_ADDRESS)
01459     extern void *STACK_END_ADDRESS;
01460     rb_gc_stack_start = STACK_END_ADDRESS;
01461 #else
01462     if (!addr) addr = (VALUE *)&addr;
01463     STACK_UPPER(&addr, addr, ++addr);
01464     if (rb_gc_stack_start) {
01465         if (STACK_UPPER(&addr,
01466                         rb_gc_stack_start > addr,
01467                         rb_gc_stack_start < addr))
01468             rb_gc_stack_start = addr;
01469         return;
01470     }
01471     rb_gc_stack_start = addr;
01472 #endif
01473 #ifdef HAVE_GETRLIMIT
01474     {
01475         struct rlimit rlim;
01476 
01477         if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
01478             unsigned int space = rlim.rlim_cur/5;
01479 
01480             if (space > 1024*1024) space = 1024*1024;
01481             STACK_LEVEL_MAX = (rlim.rlim_cur - space) / sizeof(VALUE);
01482         }
01483     }
01484 #endif
01485 }
01486 
01487 
01488 /*
01489  * Document-class: ObjectSpace
01490  *
01491  *  The <code>ObjectSpace</code> module contains a number of routines
01492  *  that interact with the garbage collection facility and allow you to
01493  *  traverse all living objects with an iterator.
01494  *
01495  *  <code>ObjectSpace</code> also provides support for object
01496  *  finalizers, procs that will be called when a specific object is
01497  *  about to be destroyed by garbage collection.
01498  *
01499  *     include ObjectSpace
01500  *
01501  *
01502  *     a = "A"
01503  *     b = "B"
01504  *     c = "C"
01505  *
01506  *
01507  *     define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" })
01508  *     define_finalizer(a, proc {|id| puts "Finalizer two on #{id}" })
01509  *     define_finalizer(b, proc {|id| puts "Finalizer three on #{id}" })
01510  *
01511