123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- --- gc.c.orig 2006-08-25 10:12:46.000000000 +0200
- +++ gc.c 2007-05-06 10:55:19.000000000 +0200
- @@ -22,8 +22,16 @@
- #include <setjmp.h>
- #include <sys/types.h>
-
- +#ifdef _WIN32
- +#include <string.h>
- +#else
- +#include <strings.h>
- +#endif
- +
- #ifdef HAVE_SYS_TIME_H
- #include <sys/time.h>
- +#elif defined(_WIN32)
- +#include <time.h>
- #endif
-
- #ifdef HAVE_SYS_RESOURCE_H
- @@ -54,7 +62,6 @@
- #if !defined(setjmp) && defined(HAVE__SETJMP)
- #define setjmp(env) _setjmp(env)
- #endif
- -
- /* Make alloca work the best possible way. */
- #ifdef __GNUC__
- # ifndef atarist
- @@ -173,8 +180,17 @@
- RUBY_CRITICAL(free(x));
- }
-
- +#if HAVE_LONG_LONG
- +#define GC_TIME_TYPE LONG_LONG
- +#else
- +#define GC_TIME_TYPE long
- +#endif
- +
- extern int ruby_in_compile;
- static int dont_gc;
- +static int gc_statistics = 0;
- +static GC_TIME_TYPE gc_time = 0;
- +static int gc_collections = 0;
- static int during_gc;
- static int need_call_final = 0;
- static st_table *finalizer_table = 0;
- @@ -209,7 +225,7 @@
- * Disables garbage collection, returning <code>true</code> if garbage
- * collection was already disabled.
- *
- - * GC.disable #=> false
- + * GC.disable #=> false or true
- * GC.disable #=> true
- *
- */
- @@ -223,6 +239,104 @@
- return old;
- }
-
- +/*
- + * call-seq:
- + * GC.enable_stats => true or false
- + *
- + * Enables garbage collection statistics, returning <code>true</code> if garbage
- + * collection statistics was already enabled.
- + *
- + * GC.enable_stats #=> false or true
- + * GC.enable_stats #=> true
- + *
- + */
- +
- +VALUE
- +rb_gc_enable_stats()
- +{
- + int old = gc_statistics;
- + gc_statistics = Qtrue;
- + return old;
- +}
- +
- +/*
- + * call-seq:
- + * GC.disable_stats => true or false
- + *
- + * Disables garbage collection statistics, returning <code>true</code> if garbage
- + * collection statistics was already disabled.
- + *
- + * GC.disable_stats #=> false or true
- + * GC.disable_stats #=> true
- + *
- + */
- +
- +VALUE
- +rb_gc_disable_stats()
- +{
- + int old = gc_statistics;
- + gc_statistics = Qfalse;
- + return old;
- +}
- +
- +/*
- + * call-seq:
- + * GC.clear_stats => nil
- + *
- + * Clears garbage collection statistics, returning nil. This resets the number
- + * of collections (GC.collections) and the time used (GC.time) to 0.
- + *
- + * GC.clear_stats #=> nil
- + *
- + */
- +
- +VALUE
- +rb_gc_clear_stats()
- +{
- + gc_collections = 0;
- + gc_time = 0;
- + return Qnil;
- +}
- +
- +/*
- + * call-seq:
- + * GC.collections => Integer
- + *
- + * Returns the number of garbage collections performed while GC statistics collection
- + * was enabled.
- + *
- + * GC.collections #=> 35
- + *
- + */
- +
- +VALUE
- +rb_gc_collections()
- +{
- + return INT2NUM(gc_collections);
- +}
- +
- +/*
- + * call-seq:
- + * GC.time => Integer
- + *
- + * Returns the time spent during garbage collection while GC statistics collection
- + * was enabled (in micro seconds).
- + *
- + * GC.time #=> 20000
- + *
- + */
- +
- +VALUE
- +rb_gc_time()
- +{
- +#if HAVE_LONG_LONG
- + return LL2NUM(gc_time);
- +#else
- + return LONG2NUM(gc_time);
- +#endif
- +}
- +
- +
- VALUE rb_mGC;
-
- static struct gc_list {
- @@ -314,7 +428,7 @@
- static RVALUE *freelist = 0;
- static RVALUE *deferred_final_list = 0;
-
- -#define HEAPS_INCREMENT 10
- +static int heaps_increment = 10;
- static struct heaps_slot {
- void *membase;
- RVALUE *slot;
- @@ -323,13 +437,165 @@
- static int heaps_length = 0;
- static int heaps_used = 0;
-
- -#define HEAP_MIN_SLOTS 10000
- -static int heap_slots = HEAP_MIN_SLOTS;
- +static int heap_min_slots = 10000;
- +static int heap_slots = 10000;
- +
- +static int heap_free_min = 4096;
- +static int heap_slots_increment = 10000;
- +static double heap_slots_growth_factor = 1.8;
- +
- +static long initial_malloc_limit = GC_MALLOC_LIMIT;
-
- -#define FREE_MIN 4096
- +static int verbose_gc_stats = Qfalse;
- +
- +static FILE* gc_data_file = NULL;
-
- static RVALUE *himem, *lomem;
-
- +static void set_gc_parameters()
- +{
- + char *gc_stats_ptr, *min_slots_ptr, *free_min_ptr, *heap_slots_incr_ptr,
- + *heap_incr_ptr, *malloc_limit_ptr, *gc_heap_file_ptr, *heap_slots_growth_factor_ptr;
- +
- + gc_data_file = stderr;
- +
- + gc_stats_ptr = getenv("RUBY_GC_STATS");
- + if (gc_stats_ptr != NULL) {
- + int gc_stats_i = atoi(gc_stats_ptr);
- + if (gc_stats_i > 0) {
- + verbose_gc_stats = Qtrue;
- + }
- + }
- +
- + gc_heap_file_ptr = getenv("RUBY_GC_DATA_FILE");
- + if (gc_heap_file_ptr != NULL) {
- + FILE* data_file = fopen(gc_heap_file_ptr, "w");
- + if (data_file != NULL) {
- + gc_data_file = data_file;
- + }
- + else {
- + fprintf(stderr,
- + "can't open gc log file %s for writing, using default\n", gc_heap_file_ptr);
- + }
- + }
- +
- + min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
- + if (min_slots_ptr != NULL) {
- + int min_slots_i = atoi(min_slots_ptr);
- + if (verbose_gc_stats) {
- + fprintf(gc_data_file, "RUBY_HEAP_MIN_SLOTS=%s\n", min_slots_ptr);
- + }
- + if (min_slots_i > 0) {
- + heap_slots = min_slots_i;
- + heap_min_slots = min_slots_i;
- + }
- + }
- +
- + free_min_ptr = getenv("RUBY_HEAP_FREE_MIN");
- + if (free_min_ptr != NULL) {
- + int free_min_i = atoi(free_min_ptr);
- + if (verbose_gc_stats) {
- + fprintf(gc_data_file, "RUBY_HEAP_FREE_MIN=%s\n", free_min_ptr);
- + }
- + if (free_min_i > 0) {
- + heap_free_min = free_min_i;
- + }
- + }
- +
- + heap_incr_ptr = getenv("RUBY_HEAP_INCREMENT");
- + if (heap_incr_ptr != NULL) {
- + int heap_incr_i = atoi(heap_incr_ptr);
- + if (verbose_gc_stats) {
- + fprintf(gc_data_file, "RUBY_HEAP_INCREMENT=%s\n", heap_incr_ptr);
- + }
- + if (heap_incr_i > 0) {
- + heaps_increment = heap_incr_i;
- + }
- + }
- +
- + heap_slots_incr_ptr = getenv("RUBY_HEAP_SLOTS_INCREMENT");
- + if (heap_slots_incr_ptr != NULL) {
- + int heap_slots_incr_i = atoi(heap_slots_incr_ptr);
- + if (verbose_gc_stats) {
- + fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", heap_slots_incr_ptr);
- + }
- + if (heap_slots_incr_i > 0) {
- + heap_slots_increment = heap_slots_incr_i;
- + }
- + }
- +
- + heap_slots_growth_factor_ptr = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR");
- + if (heap_slots_growth_factor_ptr != NULL) {
- + double heap_slots_growth_factor_d = atoi(heap_slots_growth_factor_ptr);
- + if (verbose_gc_stats) {
- + fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", heap_slots_growth_factor_ptr);
- + }
- + if (heap_slots_growth_factor_d > 0) {
- + heap_slots_growth_factor = heap_slots_growth_factor_d;
- + }
- + }
- +
- + malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
- + if (malloc_limit_ptr != NULL) {
- + int malloc_limit_i = atol(malloc_limit_ptr);
- + if (verbose_gc_stats) {
- + fprintf(gc_data_file, "RUBY_GC_MALLOC_LIMIT=%s\n", malloc_limit_ptr);
- + }
- + if (malloc_limit_i > 0) {
- + initial_malloc_limit = malloc_limit_i;
- + }
- + }
- +}
- +
- +/*
- + * call-seq:
- + * GC.dump => nil
- + *
- + * dumps information about the current GC data structures to the GC log file
- + *
- + * GC.dump #=> nil
- + *
- + */
- +
- +VALUE
- +rb_gc_dump()
- +{
- + int i;
- +
- + for (i = 0; i < heaps_used; i++) {
- + int heap_size = heaps[i].limit;
- + fprintf(gc_data_file, "HEAP[%2d]: size=%7d\n", i, heap_size);
- + }
- +
- + return Qnil;
- +}
- +
- +/*
- + * call-seq:
- + * GC.log String => String
- + *
- + * Logs string to the GC data file and returns it.
- + *
- + * GC.log "manual GC call" #=> "manual GC call"
- + *
- + */
- +
- +VALUE
- +rb_gc_log(self, original_str)
- + VALUE self, original_str;
- +{
- + if (original_str == Qnil) {
- + fprintf(gc_data_file, "\n");
- + }
- + else {
- + VALUE str = StringValue(original_str);
- + char *p = RSTRING(str)->ptr;
- + fprintf(gc_data_file, "%s\n", p);
- + }
- + return original_str;
- +}
- +
- +
- static void
- add_heap()
- {
- @@ -340,7 +606,7 @@
- struct heaps_slot *p;
- int length;
-
- - heaps_length += HEAPS_INCREMENT;
- + heaps_length += heaps_increment;
- length = heaps_length*sizeof(struct heaps_slot);
- RUBY_CRITICAL(
- if (heaps_used > 0) {
- @@ -356,10 +622,10 @@
- for (;;) {
- RUBY_CRITICAL(p = (RVALUE*)malloc(sizeof(RVALUE)*(heap_slots+1)));
- if (p == 0) {
- - if (heap_slots == HEAP_MIN_SLOTS) {
- + if (heap_slots == heap_min_slots) {
- rb_memerror();
- }
- - heap_slots = HEAP_MIN_SLOTS;
- + heap_slots = heap_min_slots;
- continue;
- }
- heaps[heaps_used].membase = p;
- @@ -375,8 +641,9 @@
- if (lomem == 0 || lomem > p) lomem = p;
- if (himem < pend) himem = pend;
- heaps_used++;
- - heap_slots *= 1.8;
- - if (heap_slots <= 0) heap_slots = HEAP_MIN_SLOTS;
- + heap_slots += heap_slots_increment;
- + heap_slots_increment *= heap_slots_growth_factor;
- + if (heap_slots <= 0) heap_slots = heap_min_slots;
-
- while (p < pend) {
- p->as.free.flags = 0;
- @@ -1026,6 +1293,39 @@
- }
- }
-
- +static char* obj_type(int tp)
- +{
- + switch (tp) {
- + case T_NIL : return "NIL";
- + case T_OBJECT : return "OBJECT";
- + case T_CLASS : return "CLASS";
- + case T_ICLASS : return "ICLASS";
- + case T_MODULE : return "MODULE";
- + case T_FLOAT : return "FLOAT";
- + case T_STRING : return "STRING";
- + case T_REGEXP : return "REGEXP";
- + case T_ARRAY : return "ARRAY";
- + case T_FIXNUM : return "FIXNUM";
- + case T_HASH : return "HASH";
- + case T_STRUCT : return "STRUCT";
- + case T_BIGNUM : return "BIGNUM";
- + case T_FILE : return "FILE";
- +
- + case T_TRUE : return "TRUE";
- + case T_FALSE : return "FALSE";
- + case T_DATA : return "DATA";
- + case T_MATCH : return "MATCH";
- + case T_SYMBOL : return "SYMBOL";
- +
- + case T_BLKTAG : return "BLKTAG";
- + case T_UNDEF : return "UNDEF";
- + case T_VARMAP : return "VARMAP";
- + case T_SCOPE : return "SCOPE";
- + case T_NODE : return "NODE";
- + default: return "____";
- + }
- +}
- +
- static void
- free_unused_heaps()
- {
- @@ -1056,12 +1356,21 @@
- unsigned long live = 0;
- unsigned long free_min = 0;
-
- + unsigned long really_freed = 0;
- + int free_counts[256];
- + int live_counts[256];
- + int do_gc_stats = gc_statistics & verbose_gc_stats;
- +
- for (i = 0; i < heaps_used; i++) {
- free_min += heaps[i].limit;
- }
- free_min = free_min * 0.2;
- - if (free_min < FREE_MIN)
- - free_min = FREE_MIN;
- + if (free_min < heap_free_min)
- + free_min = heap_free_min;
- +
- + if (do_gc_stats) {
- + for (i = 0 ; i< 256; i++) { free_counts[i] = live_counts[i] = 0; }
- + }
-
- if (ruby_in_compile && ruby_parser_stack_on_heap()) {
- /* should not reclaim nodes during compilation
- @@ -1094,6 +1403,9 @@
- if (!(p->as.basic.flags & FL_MARK)) {
- if (p->as.basic.flags) {
- obj_free((VALUE)p);
- + if (do_gc_stats) {
- + really_freed++;
- + }
- }
- if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
- p->as.free.flags = FL_MARK; /* remain marked */
- @@ -1101,6 +1413,12 @@
- final_list = p;
- }
- else {
- + if (do_gc_stats) {
- + int obt = p->as.basic.flags & T_MASK;
- + if (obt) {
- + free_counts[obt]++;
- + }
- + }
- p->as.free.flags = 0;
- p->as.free.next = freelist;
- freelist = p;
- @@ -1114,6 +1432,9 @@
- else {
- RBASIC(p)->flags &= ~FL_MARK;
- live++;
- + if (do_gc_stats) {
- + live_counts[RANY((VALUE)p)->as.basic.flags & T_MASK]++;
- + }
- }
- p++;
- }
- @@ -1132,7 +1453,7 @@
- }
- if (malloc_increase > malloc_limit) {
- malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
- - if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
- + if (malloc_limit < initial_malloc_limit) malloc_limit = initial_malloc_limit;
- }
- malloc_increase = 0;
- if (freed < free_min) {
- @@ -1140,6 +1461,20 @@
- }
- during_gc = 0;
-
- + if (do_gc_stats) {
- + fprintf(gc_data_file, "objects processed: %.7d\n", live+freed);
- + fprintf(gc_data_file, "live objects : %.7d\n", live);
- + fprintf(gc_data_file, "freelist objects : %.7d\n", freed - really_freed);
- + fprintf(gc_data_file, "freed objects : %.7d\n", really_freed);
- + for(i=0; i<256; i++) {
- + if (free_counts[i]>0) {
- + fprintf(gc_data_file,
- + "kept %.7d / freed %.7d objects of type %s\n",
- + live_counts[i], free_counts[i], obj_type(i));
- + }
- + }
- + }
- +
- /* clear finalization list */
- if (final_list) {
- deferred_final_list = final_list;
- @@ -1334,6 +1669,7 @@
- struct gc_list *list;
- struct FRAME * volatile frame; /* gcc 2.7.2.3 -O2 bug?? */
- jmp_buf save_regs_gc_mark;
- + struct timeval gctv1, gctv2;
- SET_STACK_END;
-
- #ifdef HAVE_NATIVETHREAD
- @@ -1350,6 +1686,14 @@
- if (during_gc) return;
- during_gc++;
-
- + if (gc_statistics) {
- + gc_collections++;
- + gettimeofday(&gctv1, NULL);
- + if (verbose_gc_stats) {
- + fprintf(gc_data_file, "Garbage collection started\n");
- + }
- + }
- +
- init_mark_stack();
-
- gc_mark((VALUE)ruby_current_node, 0);
- @@ -1438,6 +1782,17 @@
- } while (!MARK_STACK_EMPTY);
-
- gc_sweep();
- +
- + if (gc_statistics) {
- + GC_TIME_TYPE musecs_used;
- + gettimeofday(&gctv2, NULL);
- + musecs_used = ((GC_TIME_TYPE)(gctv2.tv_sec - gctv1.tv_sec) * 1000000) + (gctv2.tv_usec - gctv1.tv_usec);
- + gc_time += musecs_used;
- +
- + if (verbose_gc_stats) {
- + fprintf(gc_data_file, "GC time: %d msec\n", musecs_used / 1000);
- + }
- + }
- }
-
- void
- @@ -1551,6 +1906,7 @@
- if (!rb_gc_stack_start) {
- Init_stack(0);
- }
- + set_gc_parameters();
- add_heap();
- }
-
- @@ -2020,6 +2376,14 @@
- 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, "enable_stats", rb_gc_enable_stats, 0);
- + rb_define_singleton_method(rb_mGC, "disable_stats", rb_gc_disable_stats, 0);
- + rb_define_singleton_method(rb_mGC, "clear_stats", rb_gc_clear_stats, 0);
- + rb_define_singleton_method(rb_mGC, "collections", rb_gc_collections, 0);
- + rb_define_singleton_method(rb_mGC, "time", rb_gc_time, 0);
- + rb_define_singleton_method(rb_mGC, "dump", rb_gc_dump, 0);
- + rb_define_singleton_method(rb_mGC, "log", rb_gc_log, 1);
- +
- rb_mObSpace = rb_define_module("ObjectSpace");
- rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
- rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
|