Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:cgeb
ruby-railsexpress
01-railsbench-gc-patch.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 01-railsbench-gc-patch.patch of Package ruby-railsexpress
diff --git a/gc.c b/gc.c index 564d260..27d9177 100644 --- gc.c.orig +++ gc.c @@ -78,6 +78,17 @@ void *alloca (); #define GC_MALLOC_LIMIT 8000000 #endif +#ifndef HAVE_LONG_LONG +#define LONG_LONG long +#endif + +static size_t heap_free_min = 4096; +static int heap_min_slots = 10000; +static int heap_slots_increment = 10000; +static int initial_heap_slots_increment = 10000; +static double heap_slots_growth_factor = 1.8; +static size_t initial_malloc_limit = GC_MALLOC_LIMIT; + #define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory] #define MARK_STACK_MAX 1024 @@ -267,7 +278,7 @@ typedef struct RVALUE { struct RComplex complex; } as; #ifdef GC_DEBUG - const char *file; + VALUE file; int line; #endif } RVALUE; @@ -314,6 +325,8 @@ typedef struct rb_objspace { struct { int dont_gc; int during_gc; + int gc_statistics; + int verbose_gc_stats; } flags; struct { st_table *table; @@ -334,6 +347,14 @@ typedef struct rb_objspace { struct gc_list *global_list; unsigned int count; int gc_stress; + long heap_size; + unsigned LONG_LONG gc_time_accumulator; + FILE* gc_data_file; + long gc_collections; + unsigned LONG_LONG gc_allocated_size; + unsigned LONG_LONG gc_num_allocations; + unsigned long live_objects; + unsigned LONG_LONG allocated_objects; } rb_objspace_t; #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE @@ -357,6 +378,16 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress; #define heaps_freed objspace->heap.freed #define dont_gc objspace->flags.dont_gc #define during_gc objspace->flags.during_gc +#define gc_statistics objspace->flags.gc_statistics +#define verbose_gc_stats objspace->flags.verbose_gc_stats +#define heap_size objspace->heap_size +#define gc_time_accumulator objspace->gc_time_accumulator +#define gc_data_file objspace->gc_data_file +#define gc_collections objspace->gc_collections +#define gc_allocated_size objspace->gc_allocated_size +#define gc_num_allocations objspace->gc_num_allocations +#define live_objects objspace->live_objects +#define allocated_objects objspace->allocated_objects #define finalizer_table objspace->final.table #define deferred_final_list objspace->final.deferred #define mark_stack objspace->markstack.buffer @@ -375,7 +406,8 @@ rb_objspace_alloc(void) { rb_objspace_t *objspace = malloc(sizeof(rb_objspace_t)); memset(objspace, 0, sizeof(*objspace)); - malloc_limit = GC_MALLOC_LIMIT; + malloc_limit = initial_malloc_limit; + ruby_gc_stress = ruby_initial_gc_stress; return objspace; @@ -409,23 +441,8 @@ rb_objspace_free(rb_objspace_t *objspace) } #endif -/* tiny heap size */ -/* 32KB */ -/*#define HEAP_SIZE 0x8000 */ -/* 128KB */ -/*#define HEAP_SIZE 0x20000 */ -/* 64KB */ -/*#define HEAP_SIZE 0x10000 */ -/* 16KB */ -#define HEAP_SIZE 0x4000 -/* 8KB */ -/*#define HEAP_SIZE 0x2000 */ -/* 4KB */ -/*#define HEAP_SIZE 0x1000 */ -/* 2KB */ -/*#define HEAP_SIZE 0x800 */ - -#define HEAP_OBJ_LIMIT (HEAP_SIZE / sizeof(struct RVALUE)) +#define HEAP_OBJ_LIMIT 8000 +#define HEAP_SIZE (HEAP_OBJ_LIMIT * sizeof(struct RVALUE)) extern VALUE rb_cMutex; extern st_table *rb_class_tbl; @@ -673,6 +690,11 @@ vm_xmalloc(rb_objspace_t *objspace, size_t size) mem = (size_t *)mem + 1; #endif + if (gc_statistics) { + gc_allocated_size += size; + gc_num_allocations += 1; + } + return mem; } @@ -780,6 +802,92 @@ ruby_xfree(void *x) vm_xfree(&rb_objspace, x); } +static void set_gc_parameters(rb_objspace_t *objspace) +{ + char *envp; + + gc_data_file = stderr; + + envp = getenv("RUBY_GC_STATS"); + if (envp != NULL) { + int i = atoi(envp); + if (i > 0) { + verbose_gc_stats = 1; + } + /* child processes should not inherit RUBY_GC_STATS */ + ruby_unsetenv("RUBY_GC_STATS"); + } + + envp = getenv("RUBY_GC_DATA_FILE"); + if (envp != NULL) { + FILE* data_file = fopen(envp, "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", envp); + } + /* child processes should not inherit RUBY_GC_DATA_FILE to avoid clobbering */ + ruby_unsetenv("RUBY_GC_DATA_FILE"); + } + + envp = getenv("RUBY_HEAP_MIN_SLOTS"); + if (envp != NULL) { + int i = atoi(envp); + if (verbose_gc_stats) { + fprintf(gc_data_file, "RUBY_HEAP_MIN_SLOTS=%s\n", envp); + } + if (i > 0) { + heap_min_slots = i; + } + } + + envp = getenv("RUBY_HEAP_FREE_MIN"); + if (envp != NULL) { + int i = atoi(envp); + if (verbose_gc_stats) { + fprintf(gc_data_file, "RUBY_HEAP_FREE_MIN=%s\n", envp); + } + if (i > 0) { + heap_free_min = i; + } + } + + envp = getenv("RUBY_HEAP_SLOTS_INCREMENT"); + if (envp != NULL) { + int i = atoi(envp); + if (verbose_gc_stats) { + fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", envp); + } + heap_slots_increment = i; + initial_heap_slots_increment = heap_slots_increment; + } + + envp = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR"); + if (envp != NULL) { + double d = atof(envp); + if (verbose_gc_stats) { + fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", envp); + } + if (d > 0) { + heap_slots_growth_factor = d; + } + } + + envp = getenv("RUBY_GC_MALLOC_LIMIT"); + if (envp != NULL) { + long l = atol(envp); + if (verbose_gc_stats) { + fprintf(gc_data_file, "RUBY_GC_MALLOC_LIMIT=%s\n", envp); + } + if (l > 0) { + initial_malloc_limit = l; + malloc_limit = initial_malloc_limit; + } + } + + fflush(gc_data_file); +} /* * call-seq: @@ -826,6 +934,454 @@ rb_gc_disable(void) return old ? Qtrue : Qfalse; } +/* + * 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() +{ + rb_objspace_t *objspace = &rb_objspace; + int old = gc_statistics; + gc_statistics = 1; + return old ? Qtrue : Qfalse; +} + +/* + * 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() +{ + rb_objspace_t *objspace = &rb_objspace; + int old = gc_statistics; + gc_statistics = 0; + return old ? Qtrue : Qfalse; +} + +/* + * call-seq: + * GC.stats_enabled? => true or false + * + * Check whether GC stats have been enabled. + * + * GC.stats_enabled? #=> false or true + * + */ + +VALUE +rb_gc_stats_enabled() +{ + rb_objspace_t *objspace = &rb_objspace; + return gc_statistics ? Qtrue : Qfalse; +} + + +/* + * 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() +{ + rb_objspace_t *objspace = &rb_objspace; + gc_collections = 0; + gc_time_accumulator = 0; + gc_allocated_size = 0; + gc_num_allocations = 0; + return Qnil; +} + +/* + * call-seq: + * GC.allocated_size => Integer + * + * Returns the size of memory (in bytes) allocated since GC statistics collection + * was enabled. + * + * GC.allocated_size #=> 35 + * + */ + +VALUE +rb_gc_allocated_size() +{ + rb_objspace_t *objspace = &rb_objspace; +#if HAVE_LONG_LONG + return ULL2NUM(gc_allocated_size); +#else + return ULONG2NUM(gc_allocated_size); +#endif +} + +/* + * call-seq: + * GC.num_allocations => Integer + * + * Returns the number of memory allocations since GC statistics collection + * was enabled. + * + * GC.num_allocations #=> 150 + * + */ +VALUE +rb_gc_num_allocations() +{ + rb_objspace_t *objspace = &rb_objspace; +#if HAVE_LONG_LONG + return ULL2NUM(gc_num_allocations); +#else + return ULONG2NUM(gc_num_allocations); +#endif +} + +/* + * call-seq: + * GC.enable_trace => true or false + * + * Enables garbage collection tracing, returning <code>true</code> if garbage + * collection tracing was already enabled. + * + * GC.enable_trace #=> false or true + * GC.enable_trace #=> true + * + */ + +VALUE +rb_gc_enable_trace() +{ + rb_objspace_t *objspace = &rb_objspace; + int old = verbose_gc_stats; + verbose_gc_stats = 1; + return old ? Qtrue : Qfalse; +} + +/* + * call-seq: + * GC.disable_trace => true or false + * + * Disables garbage collection tracing, returning <code>true</code> if garbage + * collection tracing was already disabled. + * + * GC.disable_trace #=> false or true + * GC.disable_trace #=> true + * + */ + +VALUE +rb_gc_disable_trace() +{ + rb_objspace_t *objspace = &rb_objspace; + int old = verbose_gc_stats; + verbose_gc_stats = 0; + return old ? Qtrue : Qfalse; +} + +/* + * call-seq: + * GC.trace_enabled? => true or false + * + * Check whether GC tracing has been enabled. + * + * GC.trace_enabled? #=> false or true + * + */ + +VALUE +rb_gc_trace_enabled() +{ + rb_objspace_t *objspace = &rb_objspace; + return verbose_gc_stats ? Qtrue : Qfalse; +} + + +const char* GC_LOGFILE_IVAR = "@gc_logfile_name"; + +/* + * call-seq: + * GC.log_file(filename=nil, mode="w") => boolean + * + * Changes the GC data log file. Closes the currently open logfile. + * Returns true if the file was successfully opened for + * writing. Returns false if the file could not be opened for + * writing. Returns the name of the current logfile (or nil) if no + * parameter is given. Restores logging to stderr when given nil as + * an argument. + * + * GC.log_file #=> nil + * GC.log_file "/tmp/gc.log" #=> true + * GC.log_file #=> "/tmp/gc.log" + * GC.log_file nil #=> true + * + */ + +VALUE +rb_gc_log_file(int argc, VALUE *argv, VALUE self) +{ + rb_objspace_t *objspace = &rb_objspace; + VALUE filename = Qnil; + VALUE mode_str = Qnil; + FILE* f = NULL; + const char* mode = "w"; + + VALUE current_logfile_name = rb_iv_get(rb_mGC, GC_LOGFILE_IVAR); + + if (argc==0) + return current_logfile_name; + + rb_scan_args(argc, argv, "02", &filename, &mode_str); + + if (filename == Qnil) { + /* close current logfile and reset logfile to stderr */ + if (gc_data_file != stderr) { + fclose(gc_data_file); + gc_data_file = stderr; + rb_iv_set(rb_mGC, GC_LOGFILE_IVAR, Qnil); + } + return Qtrue; + } + + /* we have a real logfile name */ + filename = StringValue(filename); + + if (rb_equal(current_logfile_name, filename) == Qtrue) { + /* do nothing if we get the file name we're already logging to */ + return Qtrue; + } + + /* get mode for file opening */ + if (mode_str != Qnil) + { + mode = RSTRING_PTR(StringValue(mode_str)); + } + + /* try to open file in given mode */ + if (f = fopen(RSTRING_PTR(filename), mode)) { + if (gc_data_file != stderr) { + fclose(gc_data_file); + } + gc_data_file = f; + rb_iv_set(rb_mGC, GC_LOGFILE_IVAR, filename); + } else { + return Qfalse; + } + return Qtrue; +} + +/* + * 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; +{ + rb_objspace_t *objspace = &rb_objspace; + if (original_str == Qnil) { + fprintf(gc_data_file, "\n"); + } + else { + VALUE str = StringValue(original_str); + char *p = RSTRING_PTR(str); + fprintf(gc_data_file, "%s\n", p); + } + return original_str; +} + +/* + * 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() +{ + rb_objspace_t *objspace = &rb_objspace; + size_t i; + + for (i = 0; i < heaps_used; i++) { + size_t limit = heaps[i].limit; + fprintf(gc_data_file, "HEAP[%2lu]: size=%7lu\n", (unsigned long)i, (unsigned long)limit); + } + + return Qnil; +} + +static const char* obj_type(VALUE tp); + +#ifdef GC_DEBUG +/* + * call-seq: + * GC.dump_file_and_line_info(String, boolean) => nil + * + * dumps information on which currently allocated object was created by which file and on which line + * + * GC.dump_file_and_line_info(String, boolean) #=> nil + * + * The second parameter specifies whether class names should be included in the dump. + * Note that including class names will allocate additional string objects on the heap. + * + */ + +VALUE +rb_gc_dump_file_and_line_info(int argc, VALUE *argv) +{ + rb_objspace_t *objspace = &rb_objspace; + VALUE filename, str, include_classnames = Qnil; + char *fname = NULL; + char *klass = NULL; + FILE* f = NULL; + size_t i = 0; + + rb_scan_args(argc, argv, "11", &filename, &include_classnames); + + str = StringValue(filename); + fname = RSTRING_PTR(str); + f = fopen(fname, "w"); + + for (i = 0; i < heaps_used; i++) { + RVALUE *p, *pend; + + p = heaps[i].slot; pend = p + heaps[i].limit; + for (;p < pend; p++) { + if (p->as.basic.flags) { + const char *src_filename = (p->file && p->file != Qnil )? RSTRING_PTR(p->file) : ""; + fprintf(f, "%s:%s:%d", obj_type(p->as.basic.flags & T_MASK), src_filename, (int)p->line); + // rb_obj_classname will create objects on the heap, we need a better solution + if (include_classnames == Qtrue) { + /* write the class */ + fprintf(f, ":"); + switch (BUILTIN_TYPE(p)) { + case T_NONE: + fprintf(f, "__none__"); + break; + case T_UNDEF: + fprintf(f, "__undef__"); + break; + case T_NODE: + fprintf(f, "__node__"); + break; + default: + if (!p->as.basic.klass) { + fprintf(f, "__unknown__"); + } else { + fprintf(f, "%s", rb_obj_classname((VALUE)p)); + } + } + /* print object size for some known object types */ + switch (BUILTIN_TYPE(p)) { + case T_STRING: + fprintf(f, ":%lu", RSTRING_LEN(p)); + break; + case T_ARRAY: + fprintf(f, ":%lu", RARRAY_LEN(p)); + break; + case T_HASH: + fprintf(f, ":%lu", (long unsigned int)RHASH_SIZE(p)); + break; + } + } + fprintf(f, "\n"); + } + } + } + fclose(f); + return Qnil; +} +#endif + +/* + * call-seq: + * GC.heap_slots => Integer + * + * Returns the number of heap slots available for object allocations. + * + * GC.heap_slots #=> 10000 + * + */ +VALUE +rb_gc_heap_slots() +{ + rb_objspace_t *objspace = &rb_objspace; + return LONG2NUM(heap_size); +} + + +/* + * 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() +{ + rb_objspace_t *objspace = &rb_objspace; + return LONG2NUM(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() +{ + rb_objspace_t *objspace = &rb_objspace; +#if HAVE_LONG_LONG + return LL2NUM(gc_time_accumulator); +#else + return LONG2NUM(gc_time_accumulator); +#endif +} + VALUE rb_mGC; void @@ -944,6 +1500,7 @@ assign_heap_slot(rb_objspace_t *objspace) if (lomem == 0 || lomem > p) lomem = p; if (himem < pend) himem = pend; heaps_used++; + heap_size += objs; while (p < pend) { p->as.free.flags = 0; @@ -958,7 +1515,7 @@ init_heap(rb_objspace_t *objspace) { size_t add, i; - add = HEAP_MIN_SLOTS / HEAP_OBJ_LIMIT; + add = heap_min_slots / HEAP_OBJ_LIMIT; if (!add) { add = 1; @@ -979,7 +1536,7 @@ init_heap(rb_objspace_t *objspace) static void set_heaps_increment(rb_objspace_t *objspace) { - size_t next_heaps_length = (size_t)(heaps_used * 1.8); + size_t next_heaps_length = (size_t)(heaps_used * heap_slots_growth_factor); if (next_heaps_length == heaps_used) { next_heaps_length++; @@ -1005,6 +1562,22 @@ heaps_increment(rb_objspace_t *objspace) #define RANY(o) ((RVALUE*)(o)) +#ifdef GC_DEBUG +static VALUE +_rb_sourcefile(void) +{ + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); + + if (cfp) { + return cfp->iseq->filename; + } + else { + return Qnil; + } +} +#endif + static VALUE rb_newobj_from_heap(rb_objspace_t *objspace) { @@ -1022,9 +1595,11 @@ rb_newobj_from_heap(rb_objspace_t *objspace) MEMZERO((void*)obj, RVALUE, 1); #ifdef GC_DEBUG - RANY(obj)->file = rb_sourcefile(); + RANY(obj)->file = _rb_sourcefile(); RANY(obj)->line = rb_sourceline(); #endif + live_objects++; + allocated_objects++; return obj; } @@ -1525,6 +2100,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) { register RVALUE *obj = RANY(ptr); +#ifdef GC_DEBUG + if (obj->file && obj->file != Qnil && is_pointer_to_heap(objspace, (void*)obj->file)) { + gc_mark(objspace, obj->file, lev); + } +#endif + goto marking; /* skip */ again: @@ -1534,6 +2115,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev) if (obj->as.basic.flags & FL_MARK) return; /* already marked */ obj->as.basic.flags |= FL_MARK; +#ifdef GC_DEBUG + if (obj->file && obj->file != Qnil && is_pointer_to_heap(objspace, (void*)obj->file)) { + gc_mark(objspace, obj->file, lev); + } +#endif + marking: if (FL_TEST(obj, FL_EXIVAR)) { rb_mark_generic_ivar(ptr); @@ -1851,6 +2438,39 @@ free_unused_heaps(rb_objspace_t *objspace) } } +static const char* obj_type(VALUE type) +{ + switch (type) { + 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_COMPLEX: return "COMPLEX"; + case T_RATIONAL: return "RATIONAL"; + 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_ZOMBIE : return "ZOMBIE"; + + case T_UNDEF : return "UNDEF"; + case T_NODE : return "NODE"; + default: return "____"; + } +} + static void gc_sweep(rb_objspace_t *objspace) { @@ -1859,12 +2479,27 @@ gc_sweep(rb_objspace_t *objspace) size_t i; size_t live = 0, free_min = 0, do_heap_free = 0; + long max_blocks_to_free = heaps_used - (heap_min_slots / HEAP_OBJ_LIMIT); + int freed_blocks = 0; + + unsigned long processed = 0; + unsigned long freelist_size = 0; + unsigned long zombies = 0; + unsigned long free_counts[T_MASK]; + unsigned long live_counts[T_MASK]; + int do_gc_stats = gc_statistics & verbose_gc_stats; + + if (do_gc_stats) { + MEMZERO((void*)free_counts, unsigned long, T_MASK); + MEMZERO((void*)live_counts, unsigned long, T_MASK); + } + do_heap_free = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.65); free_min = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.2); - if (free_min < FREE_MIN) { + if (free_min < heap_free_min) { do_heap_free = heaps_used * HEAP_OBJ_LIMIT; - free_min = FREE_MIN; + free_min = heap_free_min; } freelist = 0; @@ -1881,10 +2516,15 @@ gc_sweep(rb_objspace_t *objspace) p = heaps[i].slot; pend = p + heaps[i].limit; while (p < pend) { if (!(p->as.basic.flags & FL_MARK)) { + if (do_gc_stats && !p->as.basic.flags) { + /* slot was free before GC */ + freelist_size++; + } if (p->as.basic.flags && ((deferred = obj_free(objspace, (VALUE)p)) || ((FL_TEST(p, FL_FINALIZE)) && need_call_final))) { if (!deferred) { + if (do_gc_stats) zombies++; p->as.free.flags = T_ZOMBIE; RDATA(p)->dfree = 0; } @@ -1894,6 +2534,10 @@ gc_sweep(rb_objspace_t *objspace) final_num++; } else { + if (do_gc_stats) { + VALUE obt = p->as.basic.flags & T_MASK; + if (obt) free_counts[obt]++; + } add_freelist(objspace, p); free_num++; } @@ -1901,16 +2545,23 @@ gc_sweep(rb_objspace_t *objspace) else if (BUILTIN_TYPE(p) == T_ZOMBIE) { /* objects to be finalized */ /* do nothing remain marked */ + if (do_gc_stats) zombies++; } else { RBASIC(p)->flags &= ~FL_MARK; live++; + if (do_gc_stats) { + live_counts[p->as.basic.flags & T_MASK]++; + } } p++; } - if (final_num + free_num == heaps[i].limit && freed > do_heap_free) { + if (final_num + free_num == heaps[i].limit && freed > do_heap_free && freed_blocks < max_blocks_to_free) { RVALUE *pp; + freed_blocks += 1; + heap_size -= final_num + free_num; + for (pp = final_list; pp != final; pp = pp->as.free.next) { RDATA(pp)->dmark = (void (*)())(VALUE)&heaps[i]; pp->as.free.flags |= FL_SINGLETON; /* freeing page mark */ @@ -1922,11 +2573,13 @@ gc_sweep(rb_objspace_t *objspace) else { freed += free_num; } + processed += heaps[i].limit; } GC_PROF_SET_MALLOC_INFO; if (malloc_increase > malloc_limit) { malloc_limit += (size_t)((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) { @@ -1934,6 +2587,25 @@ gc_sweep(rb_objspace_t *objspace) heaps_increment(objspace); } during_gc = 0; + live_objects = live; + + /* log gc stats if requested */ + if (do_gc_stats) { + fprintf(gc_data_file, "objects processed: %.7lu\n", (unsigned long)processed); + fprintf(gc_data_file, "live objects : %.7lu\n", (unsigned long)live); + fprintf(gc_data_file, "freelist objects : %.7lu\n", (unsigned long)freelist_size); + fprintf(gc_data_file, "freed objects : %.7lu\n", (unsigned long)freed); + fprintf(gc_data_file, "zombies : %.7lu\n", (unsigned long)zombies); + for(i=0; i<T_MASK; i++) { + if (free_counts[i]>0 || live_counts[i]>0) { + fprintf(gc_data_file, + "kept %.7lu / freed %.7lu objects of type %s\n", + (unsigned long)live_counts[i], (unsigned long)free_counts[i], obj_type((int)i)); + } + } + rb_gc_dump(); + fflush(gc_data_file); + } /* clear finalization list */ if (final_list) { @@ -2140,6 +2812,7 @@ void rb_gc_mark_encodings(void); static int garbage_collect(rb_objspace_t *objspace) { + struct timeval gctv1, gctv2; struct gc_list *list; rb_thread_t *th = GET_THREAD(); INIT_GC_PROF_PARAMS; @@ -2162,6 +2835,14 @@ garbage_collect(rb_objspace_t *objspace) during_gc++; objspace->count++; + if (gc_statistics) { + gc_collections++; + gettimeofday(&gctv1, NULL); + if (verbose_gc_stats) { + fprintf(gc_data_file, "Garbage collection started\n"); + } + } + GC_PROF_TIMER_START; GC_PROF_MARK_TIMER_START; SET_STACK_END; @@ -2216,6 +2897,19 @@ garbage_collect(rb_objspace_t *objspace) GC_PROF_TIMER_STOP; if (GC_NOTIFY) printf("end garbage_collect()\n"); + + if (gc_statistics) { + unsigned LONG_LONG musecs_used; + gettimeofday(&gctv2, NULL); + musecs_used = ((unsigned LONG_LONG)(gctv2.tv_sec - gctv1.tv_sec) * 1000000) + (gctv2.tv_usec - gctv1.tv_usec); + gc_time_accumulator += musecs_used; + + if (verbose_gc_stats) { + fprintf(gc_data_file, "GC time: %lu msec\n", (unsigned long)(musecs_used / 1000)); + fflush(gc_data_file); + } + } + return TRUE; } @@ -2298,6 +2992,7 @@ Init_stack(volatile VALUE *addr) void Init_heap(void) { + set_gc_parameters(&rb_objspace); init_heap(&rb_objspace); } @@ -2982,6 +3677,49 @@ count_objects(int argc, VALUE *argv, VALUE os) return hash; } +/* call-seq: + * ObjectSpace.live_objects => number + * + * Returns the count of objects currently allocated in the system. This goes + * down after the garbage collector runs. + */ +static +VALUE os_live_objects(VALUE self) +{ + rb_objspace_t *objspace = &rb_objspace; + return ULONG2NUM(live_objects); +} + +unsigned long rb_os_live_objects() +{ + rb_objspace_t *objspace = &rb_objspace; + return live_objects; +} + +/* call-seq: + * ObjectSpace.allocated_objects => number + * + * Returns the count of objects allocated since the Ruby interpreter has + * started. This number can only increase. To know how many objects are + * currently allocated, use ObjectSpace::live_objects + */ +static +VALUE os_allocated_objects(VALUE self) +{ + rb_objspace_t *objspace = &rb_objspace; +#if defined(HAVE_LONG_LONG) + return ULL2NUM(allocated_objects); +#else + return ULONG2NUM(allocated_objects); +#endif +} + +unsigned LONG_LONG rb_os_allocated_objects() +{ + rb_objspace_t *objspace = &rb_objspace; + return allocated_objects; +} + /* * call-seq: * GC.count -> Integer @@ -3188,6 +3926,28 @@ Init_GC(void) rb_define_singleton_method(rb_mGC, "count", gc_count, 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, "stats_enabled?", rb_gc_stats_enabled, 0); + rb_define_singleton_method(rb_mGC, "clear_stats", rb_gc_clear_stats, 0); + rb_define_singleton_method(rb_mGC, "allocated_size", rb_gc_allocated_size, 0); + rb_define_singleton_method(rb_mGC, "num_allocations", rb_gc_num_allocations, 0); + rb_define_singleton_method(rb_mGC, "heap_slots", rb_gc_heap_slots, 0); + rb_define_const(rb_mGC, "HEAP_SLOT_SIZE", INT2FIX(sizeof(RVALUE))); + + rb_define_singleton_method(rb_mGC, "log", rb_gc_log, 1); + rb_define_singleton_method(rb_mGC, "log_file", rb_gc_log_file, -1); + rb_define_singleton_method(rb_mGC, "enable_trace", rb_gc_enable_trace, 0); + rb_define_singleton_method(rb_mGC, "disable_trace", rb_gc_disable_trace, 0); + rb_define_singleton_method(rb_mGC, "trace_enabled?", rb_gc_trace_enabled, 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); +#ifdef GC_DEBUG + rb_define_singleton_method(rb_mGC, "dump_file_and_line_info", rb_gc_dump_file_and_line_info, -1); +#endif + rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler"); rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0); rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0); @@ -3201,6 +3961,9 @@ Init_GC(void) rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1); rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0); + rb_define_module_function(rb_mObSpace, "live_objects", os_live_objects, 0); + rb_define_module_function(rb_mObSpace, "allocated_objects", os_allocated_objects, 0); + rb_define_module_function(rb_mObSpace, "define_finalizer", define_final, -1); rb_define_module_function(rb_mObSpace, "undefine_finalizer", undefine_final, 1); diff --git sample/test.rb.orig sample/test.rb old mode 100644 new mode 100755
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor