SpiderMonkey Garbage Collection Tips

  • リビジョンの URL スラッグ: SpiderMonkey_Garbage_Collection_Tips
  • リビジョンのタイトル: SpiderMonkey Garbage Collection Tips
  • リビジョンの ID: 372853
  • 作成日:
  • 作成者: yoshitanaka
  • 現行リビジョン いいえ
  • コメント

このリビジョンの内容

 

ガーベージコレクターの隠れた危険を避ける秘訣

  1. あらかじめ定義した局所的なRootsを使う

    JSNativeでは argv の要素は、その呼び出しの持続期間rootsになります。あなたは一時的にそれらの配列の要素に値をアサインすることができません。実際、そうすることは非常によい練習になります。 JS_ConvertArguments はこれを実行します。

    argv{{ mediawiki.external(-1) }} もまたrootの一つです。それは初期的に obj 引数の根本であり、 (a.k.a. this) obj を異なるオブジェクトへ変換させることができ、 objと取り替えて新しいオブジェクトを生成します。

    *rval も rootの一つです。

  2. もし必要であれば、より多くの局所的なrootを定義します。

    JSFunctionSpec追加メンバをいくつかの局所的rootsとして初期化します。そのときは("追加に引数") として argv{{ mediawiki.external('argc') }}, argv{{ mediawiki.external('argc+1') }}, etc を必要とします。

    For JSNativeでは、 JSFunctionSpecnargs メンバは、エンジンに少なくとも複数の多くの引数を提供することを伝えます。そのため、一般的に局所的なrootの数値として繋げる事ができます。 (argv{{ mediawiki.external('argc') }}よりむしろargv{{ mediawiki.external(3) }} というように). もしより多くの引数が渡され、それに関して注意を払わないならば(varargsスタイルの関数を書かないならば)、局所的にjsvalsの根本となる追加の引数を上書きする事ができます。

    JSFastNativeは追加の局所的なrootsを要求する事ができない。そして nargs はそれらには適用できないことを保証します。

  3. 生まれたてのひな鳥の穴(原文は newborn pegeon-hole)問題を避けるためのRoot :

    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. {{ バグ(438633) }}への注意 JS_CompileScript, JS_CompileFile, または JS_CompileFileHandle を使うコードは、JSAPI ユーザーガイドの コンパイルされたスクリプトに記述されているように新しいスクリプトのrootになります。

  5. rootされていない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 この例は秘訣#3の例でもあります。

  6. マルチスレッドアプリケーション内のモデルの要求に従う。 JS_THREADSAFEを参照して下さい。もしあなたがそれらのルールに公正に従うならば、GCは、JavaScriptのインタープリター内に他のスレッドがある間、またはJavaScriptポインタを保持しているときでも一個のスレッドの上で発生します。(GCはそれを扱えるように設計されておらず、そのときはクラッシュします)

  7. 連続した時間でGCを走らせない。 いくつかのスクリプトを実行した後で、例えば コールバックの分岐からや、またはGCスレッドから定期的に起床させてGCを動作させる事ができます。実時間での結果に気をつけて下さい。あなたがレイテンシ(反応速度)に敏感であれば。

GCのヒープ領域のダンプの方法

これらの段階を使って、すべてのGCできるitemとそれらのリンク先のすべてを見つける事ができます。

Note: SpiderMonkey 1.8では、これらの機能は新しい関数に取り替えられます。 JS_DumpHeap.

処理ステップ

  • SpiderMonkeyファイルをビルドするプロジェクト内でGC_MARK_DEBUGを定義します。
  • あなたが呼び出す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;

結果の翻訳

結果は以下のように出力されます:

061f6810 object 06202ED8 Root via global object(Root @ 0x061f6810).

これは、プライベートデータ(0x06202ED8)にJSObject (0x061f6810)はあり、クラス名"Root"は大域オブジェクト (cx->globalObject)より参照されている、ということを示しています。

ヒント

  • 結果をフィルターするために、jsgc.c内にある関数gc_dump_thingを編集する必要があります。一例として、以下のコードをメソッドの先頭に追加すると、文字列にフィルターをかけます:
if(flags & GCX_STRING)
    return;


 

オリジナルのドキュメント情報

 

このリビジョンのソースコード

<p>&nbsp;</p>
<h3 id="Tips_on_avoiding_Garbage_Collector_pitfalls" name="Tips_on_avoiding_Garbage_Collector_pitfalls">ガーベージコレクターの隠れた危険を避ける秘訣</h3>
<ol>
  <li>
    <p><b>あらかじめ定義した局所的なRootsを使う</b></p>
    <p> <code><a href="en/JSNative">JSNative</a></code>では <code>argv</code> の要素は、その呼び出しの持続期間rootsになります。あなたは一時的にそれらの配列の要素に値をアサインすることができません。実際、そうすることは非常によい練習になります。 <code><a href="en/JS_ConvertArguments">JS_ConvertArguments</a></code> はこれを実行します。</p>
    <p><code>argv{{ mediawiki.external(-1) }}</code> もまたrootの一つです。それは初期的に <code>obj</code> 引数の根本であり、 (a.k.a. <code>this</code>) <code>obj</code> を異なるオブジェクトへ変換させることができ、 <code>obj</code>と取り替えて新しいオブジェクトを生成します。</p>
    <p><code>*rval</code> も rootの一つです。</p>
  </li>
  <li>
    <p><b>もし必要であれば、より多くの局所的なrootを定義します。</b></p>
    <p> <code>JSFunctionSpec</code> の<code>追加</code>メンバをいくつかの局所的rootsとして初期化します。そのときは("追加に引数") として <code>argv{{ mediawiki.external('argc') }}</code>, <code>argv{{ mediawiki.external('argc+1') }}</code>, etc を必要とします。</p>
    <p>For <code><a href="en/JSNative">JSNative</a></code>では、  <code><a href="en/JSFunctionSpec">JSFunctionSpec</a></code> の<code>nargs</code> メンバは、エンジンに少なくとも複数の多くの引数を提供することを伝えます。そのため、一般的に局所的なrootの数値として繋げる事ができます。  (argv{{ mediawiki.external('argc') }}よりむしろargv{{ mediawiki.external(3) }} というように). もしより多くの引数が渡され、それに関して注意を払わないならば(varargsスタイルの関数を書かないならば)、局所的にjsvalsの根本となる追加の引数を上書きする事ができます。</p>
    <p><code><a href="en/JSFastNative">JSFastNative</a></code>は追加の局所的なrootsを要求する事ができない。そして <code>nargs</code> はそれらには適用できないことを保証します。</p>
  </li>
  <li>
    <p>生まれたてのひな鳥の穴(原文は newborn pegeon-hole)問題を避けるための<b>Root </b> :</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><b> {{ バグ(438633) }}への注意</b>  <code><a href="en/JS_CompileScript">JS_CompileScript</a></code>, <code><a href="en/JS_CompileFile">JS_CompileFile</a></code>, または <code><a href="en/JS_CompileFileHandle">JS_CompileFileHandle</a></code> を使うコードは、JSAPI ユーザーガイドの <a href="en/JSAPI_User_Guide#Compiled_scripts">コンパイルされたスクリプト</a>に記述されているように新しいスクリプトのrootになります。</p>
  </li>
  <li>
    <p>rootされていない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 この例は秘訣#3の例でもあります。</p>
  </li>
  <li>
    <p><b>マルチスレッドアプリケーション内のモデルの要求に従う。</b> <code><a href="en/JS_THREADSAFE">JS_THREADSAFE</a></code>を参照して下さい。もしあなたがそれらのルールに公正に従うならば、GCは、JavaScriptのインタープリター内に他のスレッドがある間、またはJavaScriptポインタを保持しているときでも一個のスレッドの上で発生します。(GCはそれを扱えるように設計されておらず、そのときはクラッシュします)</p>
  </li>
  <li>
    <p><b>連続した時間でGCを走らせない。</b> いくつかのスクリプトを実行した後で、例えば <a href="en/JS_SetBranchCallback">コールバックの分岐</a>からや、またはGCスレッドから定期的に起床させてGCを動作させる事ができます。実時間での結果に気をつけて下さい。あなたがレイテンシ(反応速度)に敏感であれば。</p>
  </li>
</ol>
<h3 id="How_to_Dump_the_GC_Heap" name="How_to_Dump_the_GC_Heap">GCのヒープ領域のダンプの方法</h3>
<p>これらの段階を使って、すべてのGCできるitemとそれらのリンク先のすべてを見つける事ができます。</p>
<div class="note">
  <b>Note:</b> SpiderMonkey 1.8では、これらの機能は新しい関数に取り替えられます。 <code><a href="en/JS_DumpHeap">JS_DumpHeap</a></code>.</div>
<h4 id="Steps" name="Steps">処理ステップ</h4>
<ul>
  <li>SpiderMonkeyファイルをビルドするプロジェクト内でGC_MARK_DEBUGを定義します。</li>
  <li>あなたが呼び出すJS_GCに以下のコードと同様のコードを追加します。</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 id="Interpreting_the_results" name="Interpreting_the_results">結果の翻訳</h4>
<p>結果は以下のように出力されます:</p>
<pre>
061f6810 object 06202ED8 Root via global object(Root @ 0x061f6810).</pre>
<p>これは、プライベートデータ(0x06202ED8)にJSObject (0x061f6810)はあり、クラス名"Root"は大域オブジェクト (cx-&gt;globalObject)より参照されている、ということを示しています。</p>
<h4 id="Hints" name="Hints"> ヒント</h4>
<ul>
  <li>結果をフィルターするために、jsgc.c内にある関数gc_dump_thingを編集する必要があります。一例として、以下のコードをメソッドの先頭に追加すると、文字列にフィルターをかけます:</li>
</ul>
<pre>
if(flags &amp; GCX_STRING)
    return;
</pre>
<p><br />
  &nbsp;</p>
<div class="originaldocinfo">
  <h2 id="Original_Document_Information" name="Original_Document_Information">オリジナルのドキュメント情報</h2>
  <ul>
    <li>Author: <a class="link-mailto" href="mailto:rginda@netscape.com">Robert Ginda</a></li>
    <li>Contributor: <a class="link-mailto" href="mailto:thehesiod@gmail.com">Alex Mohr</a></li>
    <li>Last Updated Date: January 3, 2005</li>
    <li>Copyright Information: Copyright (C) <a class="link-mailto" href="mailto:rginda@netscape.com">Robert Ginda</a></li>
  </ul>
</div>
<p>&nbsp;</p>
Revert to this revision