Tips on avoiding Garbage Collector pitfalls
-
Use predefined local roots.
In a
JSNative
, the elements ofargv
are roots for the duration of the call. You can assign temporary values to those array elements; in fact it is very good practice to do so.JS_ConvertArguments
does this.argv[-1]
is also a root; it initially roots theobj
argument (a.k.a.this
) and can be used to root a conversion ofobj
to a different object, or a new object created to replaceobj
.*rval
is also a root. -
Define more local roots if you need them.
Initialize the
extra
member ofJSFunctionSpec
to the number of local roots ("extra args") you need, then useargv[argc]
,argv[argc+1]
, etc.For
JSNative
s, thenargs
member ofJSFunctionSpec
tells the engine to provide at least that many args, so you can generally hardwire the local root indices (argv[3] rather than argv[argc]). If more arguments are passed and you don't care (you aren't writing a varargs-style function), you can just overwrite the extra arguments with the locally rooted jsvals.JSFastNative
s cannot ask for extra local roots, and thenargs
guarantee does not apply to them. -
Root as you go to avoid newborn pigeon-hole problems:
JSString *str1, *str2; /* Bad! */ str1 = JS_ValueToString(cx, argv[0]); if (!str1) return JS_FALSE; str2 = JS_ValueToString(cx, argv[1]); if (!str2) return JS_FALSE; SomethingThatMightCallTheGC(); /* Good! */ str1 = JS_ValueToString(cx, argv[0]); if (!str1) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str1); str2 = JS_ValueToString(cx, argv[1]); if (!str2) return JS_FALSE; argv[1] = STRING_TO_JSVAL(str2); SomethingThatMightCallTheGC();
-
Beware バグ 438633. Code that uses
JS_CompileScript
,JS_CompileFile
, orJS_CompileFileHandle
must root the new script as described in the JSAPI User Guide under Compiled scripts. -
Avoid malloc'ing temporary storage that contains unrooted jsvals:
/* Bad! */ jsint i, len; jsval *vec; JSString *str; JSObject *myArrayObj; len = NumberOfNativeStrings(); vec = JS_malloc(cx, len * sizeof(jsval)); if (!vec) return JS_FALSE; for (i = 0; i < len; i++) { str = JS_NewStringCopyZ(cx, GetNativeString(i)); if (!str) { JS_free(cx, vec); return JS_FALSE; } vec[i] = STRING_TO_JSVAL(str); } myArrayObj = JS_NewArrayObject(cx, len, vec); JS_free(cx, vec); if (!myArrayObj) return JS_FALSE; OtherStuffThatMightGC(); *rval = OBJECT_TO_JSVAL(myArrayObj); /* Good! */ JSObject *myArrayObj; jsint i, len; JSString *str; jsval val; myArrayObj = JS_NewArrayObject(cx, 0, NULL); if (!myArrayObj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(myArrayObj); len = NumberOfNativeStrings(); for (i = 0; i < len; i++) { str = JS_NewStringCopyZ(cx, GetNativeString(i)); if (!str) return JS_FALSE; val = STRING_TO_JSVAL(str); if (!JS_SetElement(cx, myArrayObj, i, &val)) return JS_FALSE; } OtherStuffThatMightGC();
Note that this example also shows tip #3 (root as you go).
-
Follow the request model in multithreaded applications. See
JS_THREADSAFE
. If you don't follow those rules scrupulously, GC could occur on one thread while another thread is in the JavaScript interpreter or otherwise handling JavaScript pointers. (The GC is not designed to handle that and it will crash.) -
Don't run the GC at arbitrary times. You can run the GC after some number of scripts, from a branch callback, or from a "GC thread" that wakes up periodically, for example. Beware realtime effects! Just how sensitive are you to latency?
How to Dump the GC Heap
Using these steps you can find all the GC'able items and what they're linked to.
JS_DumpHeap
.Steps
- Define GC_MARK_DEBUG in the project that builds the SpiderMonkey Files
- Add code similar to the following around your call to JS_GC
extern "C" FILE* js_DumpGCHeap; js_DumpGCHeap = fopen("c:\\jsds-roots.txt", "w"); JS_GC((*i)->jsc); fclose(js_DumpGCHeap); js_DumpGCHeap = NULL;
Interpreting the results
Results will come out like the following:
061f6810 object 06202ED8 Root via global object(Root @ 0x061f6810).
This points that the JSObject (0x061f6810) with private data (0x06202ED8) and class name "Root" is referenced by the global object (cx->globalObject).
Hints
- In order to filter results you must edit the function gc_dump_thing in jsgc.c. As an example, adding the following to the top of the method will filter out strings:
if(flags & GCX_STRING) return;
Original Document Information
- Author: Robert Ginda
- Contributor: Alex Mohr
- Last Updated Date: January 3, 2005
- Copyright Information: Copyright (C) Robert Ginda