Skip to content
This repository
Newer
Older
100644 565 lines (541 sloc) 14.099 kb
43c2ce7a »
2007-08-25 new GC patches for 1.8.6 and 1.9
1 Index: gc.c
2 ===================================================================
3 --- gc.c (revision 12920)
4 +++ gc.c (working copy)
5 @@ -22,8 +22,16 @@
6 #include <setjmp.h>
7 #include <sys/types.h>
8
9 +#ifdef _WIN32
10 +#include <string.h>
11 +#else
12 +#include <strings.h>
13 +#endif
14 +
15 #ifdef HAVE_SYS_TIME_H
16 #include <sys/time.h>
17 +#elif defined(_WIN32)
18 +#include <time.h>
19 #endif
20
21 #ifdef HAVE_SYS_RESOURCE_H
22 @@ -40,7 +48,6 @@
23 #if !defined(setjmp) && defined(HAVE__SETJMP)
24 #define setjmp(env) _setjmp(env)
25 #endif
26 -
27 /* Make alloca work the best possible way. */
28 #ifdef __GNUC__
29 # ifndef atarist
30 @@ -159,8 +166,17 @@
31 RUBY_CRITICAL(free(x));
32 }
33
34 +#if HAVE_LONG_LONG
35 +#define GC_TIME_TYPE LONG_LONG
36 +#else
37 +#define GC_TIME_TYPE long
38 +#endif
39 +
40 extern int ruby_in_compile;
41 static int dont_gc;
42 +static int gc_statistics = 0;
43 +static GC_TIME_TYPE gc_time = 0;
44 +static int gc_collections = 0;
45 static int during_gc;
46 static int need_call_final = 0;
47 static st_table *finalizer_table = 0;
48 @@ -195,7 +211,7 @@
49 * Disables garbage collection, returning <code>true</code> if garbage
50 * collection was already disabled.
51 *
52 - * GC.disable #=> false
53 + * GC.disable #=> false or true
54 * GC.disable #=> true
55 *
56 */
57 @@ -209,6 +225,104 @@
58 return old;
59 }
60
61 +/*
62 + * call-seq:
63 + * GC.enable_stats => true or false
64 + *
65 + * Enables garbage collection statistics, returning <code>true</code> if garbage
66 + * collection statistics was already enabled.
67 + *
68 + * GC.enable_stats #=> false or true
69 + * GC.enable_stats #=> true
70 + *
71 + */
72 +
73 +VALUE
74 +rb_gc_enable_stats()
75 +{
76 + int old = gc_statistics;
77 + gc_statistics = Qtrue;
78 + return old;
79 +}
80 +
81 +/*
82 + * call-seq:
83 + * GC.disable_stats => true or false
84 + *
85 + * Disables garbage collection statistics, returning <code>true</code> if garbage
86 + * collection statistics was already disabled.
87 + *
88 + * GC.disable_stats #=> false or true
89 + * GC.disable_stats #=> true
90 + *
91 + */
92 +
93 +VALUE
94 +rb_gc_disable_stats()
95 +{
96 + int old = gc_statistics;
97 + gc_statistics = Qfalse;
98 + return old;
99 +}
100 +
101 +/*
102 + * call-seq:
103 + * GC.clear_stats => nil
104 + *
105 + * Clears garbage collection statistics, returning nil. This resets the number
106 + * of collections (GC.collections) and the time used (GC.time) to 0.
107 + *
108 + * GC.clear_stats #=> nil
109 + *
110 + */
111 +
112 +VALUE
113 +rb_gc_clear_stats()
114 +{
115 + gc_collections = 0;
116 + gc_time = 0;
117 + return Qnil;
118 +}
119 +
120 +/*
121 + * call-seq:
122 + * GC.collections => Integer
123 + *
124 + * Returns the number of garbage collections performed while GC statistics collection
125 + * was enabled.
126 + *
127 + * GC.collections #=> 35
128 + *
129 + */
130 +
131 +VALUE
132 +rb_gc_collections()
133 +{
134 + return INT2NUM(gc_collections);
135 +}
136 +
137 +/*
138 + * call-seq:
139 + * GC.time => Integer
140 + *
141 + * Returns the time spent during garbage collection while GC statistics collection
142 + * was enabled (in micro seconds).
143 + *
144 + * GC.time #=> 20000
145 + *
146 + */
147 +
148 +VALUE
149 +rb_gc_time()
150 +{
151 +#if HAVE_LONG_LONG
152 + return LL2NUM(gc_time);
153 +#else
154 + return LONG2NUM(gc_time);
155 +#endif
156 +}
157 +
158 +
159 VALUE rb_mGC;
160
161 static struct gc_list {
162 @@ -300,7 +414,7 @@
163 static RVALUE *freelist = 0;
164 static RVALUE *deferred_final_list = 0;
165
166 -#define HEAPS_INCREMENT 10
167 +static int heaps_increment = 10;
168 static struct heaps_slot {
169 void *membase;
170 RVALUE *slot;
171 @@ -309,13 +423,165 @@
172 static int heaps_length = 0;
173 static int heaps_used = 0;
174
175 -#define HEAP_MIN_SLOTS 10000
176 -static int heap_slots = HEAP_MIN_SLOTS;
177 +static int heap_min_slots = 10000;
178 +static int heap_slots = 10000;
179 +
180 +static int heap_free_min = 4096;
181 +static int heap_slots_increment = 10000;
182 +static double heap_slots_growth_factor = 1.8;
183 +
184 +static long initial_malloc_limit = GC_MALLOC_LIMIT;
185 +
186 +static int verbose_gc_stats = Qfalse;
187
188 -#define FREE_MIN 4096
189 +static FILE* gc_data_file = NULL;
190
191 static RVALUE *himem, *lomem;
192
193 +static void set_gc_parameters()
194 +{
195 + char *gc_stats_ptr, *min_slots_ptr, *free_min_ptr, *heap_slots_incr_ptr,
196 + *heap_incr_ptr, *malloc_limit_ptr, *gc_heap_file_ptr, *heap_slots_growth_factor_ptr;
197 +
198 + gc_data_file = stderr;
199 +
200 + gc_stats_ptr = getenv("RUBY_GC_STATS");
201 + if (gc_stats_ptr != NULL) {
202 + int gc_stats_i = atoi(gc_stats_ptr);
203 + if (gc_stats_i > 0) {
204 + verbose_gc_stats = Qtrue;
205 + }
206 + }
207 +
208 + gc_heap_file_ptr = getenv("RUBY_GC_DATA_FILE");
209 + if (gc_heap_file_ptr != NULL) {
210 + FILE* data_file = fopen(gc_heap_file_ptr, "w");
211 + if (data_file != NULL) {
212 + gc_data_file = data_file;
213 + }
214 + else {
215 + fprintf(stderr,
216 + "can't open gc log file %s for writing, using default\n", gc_heap_file_ptr);
217 + }
218 + }
219 +
220 + min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
221 + if (min_slots_ptr != NULL) {
222 + int min_slots_i = atoi(min_slots_ptr);
223 + if (verbose_gc_stats) {
224 + fprintf(gc_data_file, "RUBY_HEAP_MIN_SLOTS=%s\n", min_slots_ptr);
225 + }
226 + if (min_slots_i > 0) {
227 + heap_slots = min_slots_i;
228 + heap_min_slots = min_slots_i;
229 + }
230 + }
231 +
232 + free_min_ptr = getenv("RUBY_HEAP_FREE_MIN");
233 + if (free_min_ptr != NULL) {
234 + int free_min_i = atoi(free_min_ptr);
235 + if (verbose_gc_stats) {
236 + fprintf(gc_data_file, "RUBY_HEAP_FREE_MIN=%s\n", free_min_ptr);
237 + }
238 + if (free_min_i > 0) {
239 + heap_free_min = free_min_i;
240 + }
241 + }
242 +
243 + heap_incr_ptr = getenv("RUBY_HEAP_INCREMENT");
244 + if (heap_incr_ptr != NULL) {
245 + int heap_incr_i = atoi(heap_incr_ptr);
246 + if (verbose_gc_stats) {
247 + fprintf(gc_data_file, "RUBY_HEAP_INCREMENT=%s\n", heap_incr_ptr);
248 + }
249 + if (heap_incr_i > 0) {
250 + heaps_increment = heap_incr_i;
251 + }
252 + }
253 +
254 + heap_slots_incr_ptr = getenv("RUBY_HEAP_SLOTS_INCREMENT");
255 + if (heap_slots_incr_ptr != NULL) {
256 + int heap_slots_incr_i = atoi(heap_slots_incr_ptr);
257 + if (verbose_gc_stats) {
258 + fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", heap_slots_incr_ptr);
259 + }
260 + if (heap_slots_incr_i > 0) {
261 + heap_slots_increment = heap_slots_incr_i;
262 + }
263 + }
264 +
265 + heap_slots_growth_factor_ptr = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR");
266 + if (heap_slots_growth_factor_ptr != NULL) {
267 + double heap_slots_growth_factor_d = atoi(heap_slots_growth_factor_ptr);
268 + if (verbose_gc_stats) {
269 + fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", heap_slots_growth_factor_ptr);
270 + }
271 + if (heap_slots_growth_factor_d > 0) {
272 + heap_slots_growth_factor = heap_slots_growth_factor_d;
273 + }
274 + }
275 +
276 + malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
277 + if (malloc_limit_ptr != NULL) {
278 + int malloc_limit_i = atol(malloc_limit_ptr);
279 + if (verbose_gc_stats) {
280 + fprintf(gc_data_file, "RUBY_GC_MALLOC_LIMIT=%s\n", malloc_limit_ptr);
281 + }
282 + if (malloc_limit_i > 0) {
283 + initial_malloc_limit = malloc_limit_i;
284 + }
285 + }
286 +}
287 +
288 +/*
289 + * call-seq:
290 + * GC.dump => nil
291 + *
292 + * dumps information about the current GC data structures to the GC log file
293 + *
294 + * GC.dump #=> nil
295 + *
296 + */
297 +
298 +VALUE
299 +rb_gc_dump()
300 +{
301 + int i;
302 +
303 + for (i = 0; i < heaps_used; i++) {
304 + int heap_size = heaps[i].limit;
305 + fprintf(gc_data_file, "HEAP[%2d]: size=%7d\n", i, heap_size);
306 + }
307 +
308 + return Qnil;
309 +}
310 +
311 +/*
312 + * call-seq:
313 + * GC.log String => String
314 + *
315 + * Logs string to the GC data file and returns it.
316 + *
317 + * GC.log "manual GC call" #=> "manual GC call"
318 + *
319 + */
320 +
321 +VALUE
322 +rb_gc_log(self, original_str)
323 + VALUE self, original_str;
324 +{
325 + if (original_str == Qnil) {
326 + fprintf(gc_data_file, "\n");
327 + }
328 + else {
329 + VALUE str = StringValue(original_str);
330 + char *p = RSTRING(str)->ptr;
331 + fprintf(gc_data_file, "%s\n", p);
332 + }
333 + return original_str;
334 +}
335 +
336 +
337 static void
338 add_heap()
339 {
340 @@ -326,7 +592,7 @@
341 struct heaps_slot *p;
342 int length;
343
344 - heaps_length += HEAPS_INCREMENT;
345 + heaps_length += heaps_increment;
346 length = heaps_length*sizeof(struct heaps_slot);
347 RUBY_CRITICAL(
348 if (heaps_used > 0) {
349 @@ -342,10 +608,10 @@
350 for (;;) {
351 RUBY_CRITICAL(p = (RVALUE*)malloc(sizeof(RVALUE)*(heap_slots+1)));
352 if (p == 0) {
353 - if (heap_slots == HEAP_MIN_SLOTS) {
354 + if (heap_slots == heap_min_slots) {
355 rb_memerror();
356 }
357 - heap_slots = HEAP_MIN_SLOTS;
358 + heap_slots = heap_min_slots;
359 continue;
360 }
361 heaps[heaps_used].membase = p;
362 @@ -361,8 +627,9 @@
363 if (lomem == 0 || lomem > p) lomem = p;
364 if (himem < pend) himem = pend;
365 heaps_used++;
366 - heap_slots *= 1.8;
367 - if (heap_slots <= 0) heap_slots = HEAP_MIN_SLOTS;
368 + heap_slots += heap_slots_increment;
369 + heap_slots_increment *= heap_slots_growth_factor;
370 + if (heap_slots <= 0) heap_slots = heap_min_slots;
371
372 while (p < pend) {
373 p->as.free.flags = 0;
374 @@ -1015,6 +1282,39 @@
375 }
376 }
377
378 +static char* obj_type(int tp)
379 +{
380 + switch (tp) {
381 + case T_NIL : return "NIL";
382 + case T_OBJECT : return "OBJECT";
383 + case T_CLASS : return "CLASS";
384 + case T_ICLASS : return "ICLASS";
385 + case T_MODULE : return "MODULE";
386 + case T_FLOAT : return "FLOAT";
387 + case T_STRING : return "STRING";
388 + case T_REGEXP : return "REGEXP";
389 + case T_ARRAY : return "ARRAY";
390 + case T_FIXNUM : return "FIXNUM";
391 + case T_HASH : return "HASH";
392 + case T_STRUCT : return "STRUCT";
393 + case T_BIGNUM : return "BIGNUM";
394 + case T_FILE : return "FILE";
395 +
396 + case T_TRUE : return "TRUE";
397 + case T_FALSE : return "FALSE";
398 + case T_DATA : return "DATA";
399 + case T_MATCH : return "MATCH";
400 + case T_SYMBOL : return "SYMBOL";
401 +
402 + case T_BLKTAG : return "BLKTAG";
403 + case T_UNDEF : return "UNDEF";
404 + case T_VARMAP : return "VARMAP";
405 + case T_SCOPE : return "SCOPE";
406 + case T_NODE : return "NODE";
407 + default: return "____";
408 + }
409 +}
410 +
411 static void
412 free_unused_heaps()
413 {
414 @@ -1045,12 +1345,21 @@
415 unsigned long live = 0;
416 unsigned long free_min = 0;
417
418 + unsigned long really_freed = 0;
419 + int free_counts[256];
420 + int live_counts[256];
421 + int do_gc_stats = gc_statistics & verbose_gc_stats;
422 +
423 for (i = 0; i < heaps_used; i++) {
424 free_min += heaps[i].limit;
425 }
426 free_min = free_min * 0.2;
427 - if (free_min < FREE_MIN)
428 - free_min = FREE_MIN;
429 + if (free_min < heap_free_min)
430 + free_min = heap_free_min;
431 +
432 + if (do_gc_stats) {
433 + for (i = 0 ; i< 256; i++) { free_counts[i] = live_counts[i] = 0; }
434 + }
435
436 if (ruby_in_compile && ruby_parser_stack_on_heap()) {
437 /* should not reclaim nodes during compilation
438 @@ -1083,6 +1392,9 @@
439 if (!(p->as.basic.flags & FL_MARK)) {
440 if (p->as.basic.flags) {
441 obj_free((VALUE)p);
442 + if (do_gc_stats) {
443 + really_freed++;
444 + }
445 }
446 if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
447 p->as.free.flags = FL_MARK; /* remain marked */
448 @@ -1090,6 +1402,12 @@
449 final_list = p;
450 }
451 else {
452 + if (do_gc_stats) {
453 + int obt = p->as.basic.flags & T_MASK;
454 + if (obt) {
455 + free_counts[obt]++;
456 + }
457 + }
458 p->as.free.flags = 0;
459 p->as.free.next = freelist;
460 freelist = p;
461 @@ -1103,6 +1421,9 @@
462 else {
463 RBASIC(p)->flags &= ~FL_MARK;
464 live++;
465 + if (do_gc_stats) {
466 + live_counts[RANY((VALUE)p)->as.basic.flags & T_MASK]++;
467 + }
468 }
469 p++;
470 }
471 @@ -1121,7 +1442,7 @@
472 }
473 if (malloc_increase > malloc_limit) {
474 malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
475 - if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
476 + if (malloc_limit < initial_malloc_limit) malloc_limit = initial_malloc_limit;
477 }
478 malloc_increase = 0;
479 if (freed < free_min) {
480 @@ -1129,6 +1450,20 @@
481 }
482 during_gc = 0;
483
484 + if (do_gc_stats) {
485 + fprintf(gc_data_file, "objects processed: %.7d\n", live+freed);
486 + fprintf(gc_data_file, "live objects : %.7d\n", live);
487 + fprintf(gc_data_file, "freelist objects : %.7d\n", freed - really_freed);
488 + fprintf(gc_data_file, "freed objects : %.7d\n", really_freed);
489 + for(i=0; i<256; i++) {
490 + if (free_counts[i]>0) {
491 + fprintf(gc_data_file,
492 + "kept %.7d / freed %.7d objects of type %s\n",
493 + live_counts[i], free_counts[i], obj_type(i));
494 + }
495 + }
496 + }
497 +
498 /* clear finalization list */
499 if (final_list) {
500 deferred_final_list = final_list;
501 @@ -1323,6 +1658,7 @@
502 struct gc_list *list;
503 struct FRAME * volatile frame; /* gcc 2.7.2.3 -O2 bug?? */
504 jmp_buf save_regs_gc_mark;
505 + struct timeval gctv1, gctv2;
506 SET_STACK_END;
507
508 #ifdef HAVE_NATIVETHREAD
509 @@ -1339,6 +1675,14 @@
510 if (during_gc) return;
511 during_gc++;
512
513 + if (gc_statistics) {
514 + gc_collections++;
515 + gettimeofday(&gctv1, NULL);
516 + if (verbose_gc_stats) {
517 + fprintf(gc_data_file, "Garbage collection started\n");
518 + }
519 + }
520 +
521 init_mark_stack();
522
523 gc_mark((VALUE)ruby_current_node, 0);
524 @@ -1414,6 +1758,17 @@
525 } while (!MARK_STACK_EMPTY);
526
527 gc_sweep();
528 +
529 + if (gc_statistics) {
530 + GC_TIME_TYPE musecs_used;
531 + gettimeofday(&gctv2, NULL);
532 + musecs_used = ((GC_TIME_TYPE)(gctv2.tv_sec - gctv1.tv_sec) * 1000000) + (gctv2.tv_usec - gctv1.tv_usec);
533 + gc_time += musecs_used;
534 +
535 + if (verbose_gc_stats) {
536 + fprintf(gc_data_file, "GC time: %d msec\n", musecs_used / 1000);
537 + }
538 + }
539 }
540
541 void
542 @@ -1595,6 +1950,7 @@
543 if (!rb_gc_stack_start) {
544 Init_stack(0);
545 }
546 + set_gc_parameters();
547 add_heap();
548 }
549
550 @@ -2064,6 +2420,14 @@
551 rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
552 rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
553
554 + rb_define_singleton_method(rb_mGC, "enable_stats", rb_gc_enable_stats, 0);
555 + rb_define_singleton_method(rb_mGC, "disable_stats", rb_gc_disable_stats, 0);
556 + rb_define_singleton_method(rb_mGC, "clear_stats", rb_gc_clear_stats, 0);
557 + rb_define_singleton_method(rb_mGC, "collections", rb_gc_collections, 0);
558 + rb_define_singleton_method(rb_mGC, "time", rb_gc_time, 0);
559 + rb_define_singleton_method(rb_mGC, "dump", rb_gc_dump, 0);
560 + rb_define_singleton_method(rb_mGC, "log", rb_gc_log, 1);
561 +
562 rb_mObSpace = rb_define_module("ObjectSpace");
563 rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
564 rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
Something went wrong with that request. Please try again.