SpiderMonkey Garbage Collection Tips

  • Revision slug: SpiderMonkey_Garbage_Collection_Tips
  • Revision title: SpiderMonkey Garbage Collection Tips
  • Revision id: 161842
  • Created:
  • Creator: Jorend
  • Is current revision? No
  • Comment /* How to Dump the GC Heap */ that should be SM1.8 not 1.9

Revision Content

Tips on avoiding Garbage Collector pitfalls.

  1. Use predefined local roots:

    argv{{mediawiki.external('i')}} for i in {{mediawiki.external('0, argc-1')}}. *rval. Even argv{{mediawiki.external(-1)}} to root a conversion of the obj (a.k.a. this) parameter to a different object, or a new object created to replace obj.

  2. Define more local roots if you need them:

    Initialize the extra member of JSFunctionSpec to the number of local roots ("extra args") you need, then use argv{{mediawiki.external('argc')}}, argv{{mediawiki.external('argc+1')}}, etc. (or just argv{{mediawiki.external(3)}} if you know the function gets 3 arguments for sure).

    N.B.: The nargs member of JSFunctionSpec tells the engine to provide at least that many args, so you can generally hardwire the local root indices (argv{{mediawiki.external(3)}} rather than argv{{mediawiki.external('argc')}}). If more args are passed and you don't care (you aren't writing a varargs-style function), you can just overwrite the extra args with the locally rooted jsvals.

  3. 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();
    
  4. 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).

  5. Important: don't run the GC at arbitrary times. You must run it only when threads that might have JS interpreter code active on their stacks are all stopped at "safe GC points". The easiest way to do this is to use JS_BeginRequest and JS_EndRequest around each thread's largest chunk of JS API usage (say, evaluating a script and converting its result into a string). You can run the GC after some number of scripts, or from a "GC thread" that wakes up periodically, e.g. 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.

Note: In SpiderMonkey 1.8, these features are being replaced with a new function, 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

Revision Source

<p>
</p>
<h3 name="Tips_on_avoiding_Garbage_Collector_pitfalls."> Tips on avoiding Garbage Collector pitfalls. </h3>
<ol>
<li>
<p>Use predefined local roots:</p>
<p>argv{{mediawiki.external('i')}} for i in {{mediawiki.external('0, argc-1')}}. *rval.
Even argv{{mediawiki.external(-1)}} to root a conversion of the obj (a.k.a. this)
parameter to a different object, or a new object created to
replace obj.</p>
</li>
<li>
<p>Define more local roots if you need them:</p>
<p>Initialize the extra member of JSFunctionSpec to the number of local
roots ("extra args") you need, then use argv{{mediawiki.external('argc')}}, argv{{mediawiki.external('argc+1')}}, etc.
(or just argv{{mediawiki.external(3)}} if you know the function gets 3 arguments for sure).</p>
<p>N.B.: The nargs member of JSFunctionSpec tells the engine to provide
at least that many args, so you can generally hardwire the local
root indices (argv{{mediawiki.external(3)}} rather than argv{{mediawiki.external('argc')}}).  If more args
are passed and you don't care (you aren't writing a varargs-style
function), you can just overwrite the extra args with the locally
rooted jsvals.</p>
</li>
<li>
<p>Root as you go to avoid newborn pigeon-hole problems:</p>
<pre class="eval">JSString *str1, *str2;

<b>/* Bad! */</b>
str1 = JS_ValueToString(cx, argv[0]);
if (!str1) return JS_FALSE;
str2 = JS_ValueToString(cx, argv[1]);
if (!str2) return JS_FALSE;
SomethingThatMightCallTheGC();

<b>/* Good! */</b>
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();
</pre>
</li>
<li>
<p>Avoid malloc'ing temporary storage that contains unrooted jsvals:</p>
<pre class="eval"><b>/* Bad! */</b>
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 &lt; 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);

<b>/* Good! */</b>
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 &lt; len; i++) {
    str = JS_NewStringCopyZ(cx, GetNativeString(i));
    if (!str) return JS_FALSE;
    val = STRING_TO_JSVAL(str);
    if (!JS_SetElement(cx, myArrayObj, i, &amp;val))
        return JS_FALSE;
}
OtherStuffThatMightGC();
</pre>
<p>Note that this example also shows tip #3 (root as you go).</p>
</li>
<li>
<p>Important: don't run the GC at arbitrary times.
You must run it only when threads that might have JS interpreter code
active on their stacks are all stopped at "safe GC points".  The
easiest way to do this is to use JS_BeginRequest and JS_EndRequest
around each thread's largest chunk of JS API usage (say, evaluating a
script and converting its result into a string).  You can run the GC
after some number of scripts, or from a "GC thread" that wakes up
periodically, e.g. Beware realtime effects!  Just how sensitive are
you to latency?</p>
</li>
</ol>
<h3 name="How_to_Dump_the_GC_Heap"> How to Dump the GC Heap </h3>
<p>Using these steps you can find all the GC'able items and what they're linked to.
</p>
<div class="note"><b>Note:</b>  In SpiderMonkey 1.8, these features are being replaced with a new function, <code><a href="en/JS_DumpHeap">JS_DumpHeap</a></code>.</div>
<h4 name="Steps"> Steps </h4>
<ul><li> Define GC_MARK_DEBUG in the project that builds the SpiderMonkey Files
</li><li> Add code similar to the following around your call to JS_GC<br>
</li></ul>
<pre>extern "C" FILE* js_DumpGCHeap;

js_DumpGCHeap = fopen("c:\\jsds-roots.txt", "w");

JS_GC((*i)-&gt;jsc);

fclose(js_DumpGCHeap);

js_DumpGCHeap = NULL;
</pre>
<h4 name="Interpreting_the_results"> Interpreting the results </h4>
<p>Results will come out like the following:
</p>
<pre>061f6810 object 06202ED8 Root via global object(Root @ 0x061f6810).</pre>
<p>This points that the JSObject (0x061f6810) with private data (0x06202ED8) and class name "Root" is referenced by the global object (cx-&gt;globalObject).
</p>
<h4 name="Hints"> Hints </h4>
<ul><li> 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:
</li></ul>
<pre>if(flags &amp; GCX_STRING)
    return;
</pre>
<p><br><br>
</p>
<div class="originaldocinfo">
<h2 name="Original_Document_Information">Original Document Information</h2>
<ul><li> Author: <a class="external" href="mailto:rginda@netscape.com">Robert Ginda</a>
</li><li> Contributor: <a class="external" href="mailto:thehesiod@gmail.com">Alex Mohr</a>
</li><li> Last Updated Date: January 3, 2005
</li><li> Copyright Information: Copyright (C) <a class="external" href="mailto:rginda@netscape.com">Robert Ginda</a>
</li></ul>
</div>
Revert to this revision