mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
gc.c: replace threshold model with debt-based GC trigger
Replace the threshold-based GC trigger (gc->threshold vs gc->live) with a debt model (gc->gc_debt). Each allocation increments debt; each GC step decrements by GC_STEP_SIZE. When a cycle completes, credit is proportional to live_after_mark * interval_ratio, giving a natural feedback loop that adapts to allocation rate. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -45,7 +45,7 @@ typedef struct mrb_gc {
|
||||
mrb_bool gray_overflow:1; /* gray stack overflowed; needs heap rescan */
|
||||
size_t live; /* count of live objects */
|
||||
size_t live_after_mark; /* old generation objects */
|
||||
size_t threshold; /* threshold to start GC */
|
||||
mrb_int gc_debt; /* <0:credit, >0:needs GC */
|
||||
size_t oldgen_threshold; /* threshold to kick major GC */
|
||||
mrb_gc_state state; /* current state of gc */
|
||||
int interval_ratio;
|
||||
|
||||
@@ -598,7 +598,8 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
|
||||
#ifdef MRB_GC_STRESS
|
||||
mrb_full_gc(mrb);
|
||||
#endif
|
||||
if (gc->threshold < gc->live) {
|
||||
gc->gc_debt++;
|
||||
if (gc->gc_debt > 0) {
|
||||
mrb_incremental_gc(mrb);
|
||||
}
|
||||
gc_arena_keep(mrb, gc);
|
||||
@@ -1331,7 +1332,7 @@ incremental_gc_step(mrb_state *mrb, mrb_gc *gc)
|
||||
break;
|
||||
}
|
||||
|
||||
gc->threshold = gc->live + GC_STEP_SIZE;
|
||||
gc->gc_debt -= (mrb_int)GC_STEP_SIZE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1380,9 +1381,11 @@ mrb_incremental_gc(mrb_state *mrb)
|
||||
if (gc->state == MRB_GC_STATE_ROOT) {
|
||||
gc->malloc_increase = 0;
|
||||
mrb_assert(gc->live >= gc->live_after_mark);
|
||||
gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
|
||||
if (gc->threshold < GC_STEP_SIZE) {
|
||||
gc->threshold = GC_STEP_SIZE;
|
||||
{
|
||||
mrb_int credit = (mrb_int)((gc->live_after_mark/100) * gc->interval_ratio)
|
||||
- (mrb_int)gc->live_after_mark;
|
||||
if (credit < (mrb_int)GC_STEP_SIZE) credit = (mrb_int)GC_STEP_SIZE;
|
||||
gc->gc_debt = -credit;
|
||||
}
|
||||
|
||||
if (is_major_gc(gc)) {
|
||||
@@ -1429,7 +1432,12 @@ mrb_full_gc(mrb_state *mrb)
|
||||
}
|
||||
|
||||
incremental_gc_finish(mrb, gc);
|
||||
gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
|
||||
{
|
||||
mrb_int credit = (mrb_int)((gc->live_after_mark/100) * gc->interval_ratio)
|
||||
- (mrb_int)gc->live_after_mark;
|
||||
if (credit < (mrb_int)GC_STEP_SIZE) credit = (mrb_int)GC_STEP_SIZE;
|
||||
gc->gc_debt = -credit;
|
||||
}
|
||||
|
||||
if (is_generational(gc)) {
|
||||
gc->oldgen_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
|
||||
@@ -1777,7 +1785,8 @@ mrb_objspace_page_slot_size(void)
|
||||
* GC.stat -> Hash
|
||||
*
|
||||
* Returns a Hash with GC statistics.
|
||||
* Keys: :live, :threshold, :state, :generational, :full
|
||||
* Keys: :live, :debt, :state, :generational, :full,
|
||||
* :step_limit, :malloc_increase, :malloc_threshold
|
||||
* With MRB_GC_STATS: :total, :minor, :major
|
||||
*
|
||||
*/
|
||||
@@ -1789,7 +1798,7 @@ gc_stat(mrb_state *mrb, mrb_value self)
|
||||
mrb_value hash = mrb_hash_new_capa(mrb, 8);
|
||||
|
||||
mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "live")), mrb_int_value(mrb, (mrb_int)gc->live));
|
||||
mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "threshold")), mrb_int_value(mrb, (mrb_int)gc->threshold));
|
||||
mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "debt")), mrb_int_value(mrb, gc->gc_debt));
|
||||
mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "state")), mrb_int_value(mrb, (mrb_int)gc->state));
|
||||
mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "generational")), mrb_bool_value(gc->generational));
|
||||
mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "full")), mrb_bool_value(gc->full));
|
||||
|
||||
Reference in New Issue
Block a user