diff --git a/jsgc.c b/jsgc.c index 9a8ceaf..62567ac 100644 --- a/jsgc.c +++ b/jsgc.c @@ -130,8 +130,8 @@ void js_gc(js_State *J, int report) js_Object *obj, *nextobj, **prevnextobj; js_String *str, *nextstr, **prevnextstr; js_Environment *env, *nextenv, **prevnextenv; - int nenv = 0, nfun = 0, nobj = 0, nstr = 0, nprop = 0; - int genv = 0, gfun = 0, gobj = 0, gstr = 0, gprop = 0; + unsigned int nenv = 0, nfun = 0, nobj = 0, nstr = 0, nprop = 0; + unsigned int genv = 0, gfun = 0, gobj = 0, gstr = 0, gprop = 0; int mark; int i; @@ -141,8 +141,6 @@ void js_gc(js_State *J, int report) return; } - J->gccounter = 0; - mark = J->gcmark = J->gcmark == 1 ? 2 : 1; /* Add initial roots. */ @@ -238,10 +236,17 @@ void js_gc(js_State *J, int report) ++nstr; } + unsigned int ntot = nenv + nfun + nobj + nstr + nprop; + unsigned int gtot = genv + gfun + gobj + gstr + gprop; + unsigned int remaining = ntot - gtot; + + J->gccounter = remaining; + J->gcthresh = remaining * JS_GCFACTOR; + if (report) { char buf[256]; - snprintf(buf, sizeof buf, "garbage collected: %d/%d envs, %d/%d funs, %d/%d objs, %d/%d props, %d/%d strs", - genv, nenv, gfun, nfun, gobj, nobj, gprop, nprop, gstr, nstr); + snprintf(buf, sizeof buf, "garbage collected (%d%%): %d/%d envs, %d/%d funs, %d/%d objs, %d/%d props, %d/%d strs", + 100*gtot/ntot, genv, nenv, gfun, nfun, gobj, nobj, gprop, nprop, gstr, nstr); js_report(J, buf); } } diff --git a/jsi.h b/jsi.h index 09975dc..0547924 100644 --- a/jsi.h +++ b/jsi.h @@ -78,8 +78,16 @@ typedef struct js_StackTrace js_StackTrace; #ifndef JS_TRYLIMIT #define JS_TRYLIMIT 64 /* exception stack size */ #endif -#ifndef JS_GCLIMIT -#define JS_GCLIMIT 10000 /* run gc cycle every N allocations */ +#ifndef JS_GCFACTOR +/* + * GC will try to trigger when memory usage is this value times the minimum + * needed memory. E.g. if there are 100 remaining objects after GC and this + * value is 5.0, then the next GC will trigger when the overall number is 500. + * I.e. a value of 5.0 aims at 80% garbage, 20% remain-used on each GC. + * The bigger the value the less impact GC has on overall performance, but more + * memory is used and individual GC pauses are longer (but fewer). + */ +#define JS_GCFACTOR 5.0 /* memory overhead factor >= 1.0 */ #endif #ifndef JS_ASTLIMIT #define JS_ASTLIMIT 100 /* max nested expressions */ @@ -233,7 +241,7 @@ struct js_State /* garbage collector list */ int gcpause; int gcmark; - int gccounter; + unsigned int gccounter, gcthresh; js_Environment *gcenv; js_Function *gcfun; js_Object *gcobj; diff --git a/jsrun.c b/jsrun.c index 9af2abc..58eaa34 100644 --- a/jsrun.c +++ b/jsrun.c @@ -1356,7 +1356,7 @@ static void jsR_run(js_State *J, js_Function *F) J->strict = F->strict; while (1) { - if (J->gccounter > JS_GCLIMIT) + if (J->gccounter > J->gcthresh) js_gc(J, 0); J->trace[J->tracetop].line = *pc++; diff --git a/jsstate.c b/jsstate.c index 32455e3..5530231 100644 --- a/jsstate.c +++ b/jsstate.c @@ -285,6 +285,7 @@ js_State *js_newstate(js_Alloc alloc, void *actx, int flags) J->gcmark = 1; J->nextref = 0; + J->gcthresh = 0; /* reaches stability within ~ 2-5 GC cycles */ J->R = jsV_newobject(J, JS_COBJECT, NULL); J->G = jsV_newobject(J, JS_COBJECT, NULL);