3

インターフェイスの不可欠な部分としてフレームを使用するサイトで動作する GreaseMonkey スクリプトがあります。このスクリプトはふるいのようにメモリ リークを起こします。これは、フレームの 1 つで addEventListener を使用したことが原因だと思います。簡単に言えば、さまざまなイベント リスナーをアタッチしてから、フレームをリロードし、イベント リスナーをアタッチしてから、フレームをリロードします。このフレームまたは他のフレーム内のさまざまな要素を操作すると、数百回または場合によっては数千回の反復が繰り返されます。それの終わりまでに、Firefox はメモリが 300M から 2G にまで増えました (または、そこに到達する前にクラッシュします)。

ページ全体をリロードすると、FireFox のガベージ コレクション ルーチンが開始され、孤立したイベント ハンドラーからすべてのメモリが回復されることをどこかで読みました。 300M に戻りました。残念ながら、これはサイトの別のフレーム (非常に人気のあるチャット ウィンドウ) を壊します。

ページ全体を強制的に更新せずにメモリを適切に解放するために他にできることはありますか?

(現在GM1.5とFF17を使用していますが、GM0.8/FF4くらいから問題が発生しています。)

4

1 に答える 1

10

完全なスクリプト、または短い自己完結型のコンパイル可能な例を見なければ、何が起こっているのか確信が持てません。それはaddEventListener問題ではないかもしれません。

以下に、メモリ リークを減らしてコードを改善するための戦略をいくつか示します。

  1. インライン/匿名関数は、多くの場合、特にイベント ハンドラーの場合に問題になります。

    悪い/漏れやすい:

    elem.onclick = function () {/*do something*/};
    elem.addEventListener ("click", function() {/*do something*/}, false);
    $("elem").click ( function () {/*do something*/} );
    

    漏れにくく、メンテナンスも簡単です。

    elem.onclick = clickHandler;
    elem.addEventListener ("click", clickHandler, false);
    $("elem").click (clickHandler);
    
    function clickHandler (evt) {
        /*do something*/
    }
    

    とにかく、ユーザースクリプトの場合は、などを避ける必要があることに注意してください。onclick

  2. 同様に、HTML 属性に JS を使用しないでください。EG 使用しない<span onclick="callSomeFunction()">など

  3. iframe で実行するコードを、明示的に必要なコードのみに最小限に抑えます。

    1. @include@exclude、およびディレクティブを使用して、@matchできるだけ多くの不要な iframe をブロックします。
    2. 次のように、iframe で実行する必要のないすべてのコードをブロックにラップします。

      if (window.top === window.self) {
        // Not in a frame
      }
      
  4. 使用しないでくださいinnerHTML

  5. 多くの要素、または AJAX と一緒に出入りする要素については、addEventListener()または jQuery の.bind().click()などを使用しないでください。
    これにより、数千のノードにわたってリスナーがレプリケートされる可能性があります。

    jQuery.on(). そうすれば、リスナーは一度だけアタッチされ、バブリングによって適切にトリガーされます。(ごくまれに.on()、ページの JavaScript によってブロックされる場合があることに注意してください。)

    あなたの場合、おそらく次のようなものが必要です。

    $(document).on ("click", "YOUR ELEM SELECTOR", clickHandler);
    
    function clickHandler (evt) {
        /*do something*/
    }
    
  6. createElement()予期せぬ循環参照や孤立したアイテムを避けるために、appendChild()、 などの直接 DOM メソッドではなく、jQuery を使用して要素を追加または削除します。jQuery は、
    そのようなことを最小限に抑えるように設計/テストされています。

  7. 使いすぎ注意GM_setValue()。大量のグローバル リソースを簡単に使用したり、スクリプト インスタンスをクラッシュさせたりする可能性があります。

    1. 同じドメイン値の場合は、 を使用しますlocalStorage
    2. GM_setValue()文字列以外の保存には使用しないでください。それ以外の場合は、 などのシリアライザーを使用しGM_SuperValueます。無害に見える整数でさえ、デフォルトGM_setValue()がクラッシュする可能性があります。
    3. 小さな変数をたくさん保存するよりも、それらをオブジェクトにラップしてシリアライザの 1 つに保存するほうがよい場合があります。


  8. 常に戻り値をチェックし、要素が欠落している可能性があると想定してください:
    これは貧弱です(そして、悲しいかな、典型的です):

    $("selector").text($("selector").text().match(/foo=([bar]+)/)[1]);
    

    より良い:

    var salesItemDiv    = $("selector");
    var fooMatch        = salesItemDiv.text ().match (/\bfoo\s*=\s*([bar]+)\b/i);
    if (fooMatch  &&  fooMatch.length > 1) {
        salesItemDiv.text ( fooMatch[1] );
    }
    

    おそらく次が続きます:

    salesItemDiv = fooMatch = null;
    

    下記参照。

  9. 再帰/インラインsetTimeout()呼び出しに注意してください。setInterval()繰り返しタイミングに使用します。イベント ハンドラーと同様に、インライン/匿名関数は使用しないでください。

  10. JSLintを介してコードを実行します。

  11. 回避eval()および自動/非表示のeval()呼び出し

  12. 変数を使い終わったら、変数を に設定nullします。たとえば、これを参照してください。

  13. 参考:「JavaScriptで何がメモリリークを起こすか知っていますか?」

  14. JS メモリ リークに関する追加情報

  15. Mozillaパフォーマンス: リークツール

于 2012-12-04T12:21:30.787 に答える