34

JavaScriptでどのようなコードでメモリリークが発生するのかを理解したいと思い、以下のスクリプトを作成しました。ただし、OS X の Safari 6.0.4 でスクリプトを実行すると、アクティビティ モニターに表示されるメモリ消費量は実際には増加しません。

私のスクリプトに何か問題がありますか、それとも最新のブラウザーではもう問題ではありませんか?

<html>
<body>
</body>
<script>
var i, el;

function attachAlert(element) {
    element.onclick = function() { alert(element.innerHTML); };
}

for (i = 0; i < 1000000; i++) {
    el = document.createElement('div');
    el.innerHTML = i;
    attachAlert(el);
}
</script>
</html>

このスクリプトは、Google の JavaScript スタイル ガイドの Closure セクションに基づいています: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Closures#Closures

編集: 上記のコードがリークする原因となったバグは明らかに修正されました: http://jibbering.com/faq/notes/closures/#clMem

しかし、私の疑問は残ります。最新のブラウザーでメモリ リークを起こす JavaScript コードの現実的な例を誰か提供できるでしょうか?

メモリ リークが複雑な単一ページ アプリケーションの問題になる可能性があることを示唆する記事がインターネット上に多数ありますが、ブラウザで実行できる例を見つけるのに苦労しています。

4

7 に答える 7

12

作成してどこでも参照した要素を保持していないため、メモリ使用量が増加していません。要素を DOM にアタッチするか、オブジェクトに格納するか、onclick を固定する別の要素に設定してみてください。その後、メモリ使用量が急上昇することがわかります。ガベージ コレクターがやってきて、参照できなくなったものをすべてクリーンアップします。

基本的にコードのウォークスルー:

  • 要素の作成 (el)
  • その要素を参照する新しい関数を作成する
  • 関数をその要素の onclick に設定します
  • 要素を新しい要素で上書きする

すべてが存在する要素を中心にしています。要素にアクセスする方法がなくなると、onclick にアクセスできなくなります。したがって、onclick にアクセスできないため、作成された関数は破棄され、関数には要素への唯一の参照があったため、要素もクリーンアップされます。

誰かがより技術的な例を持っているかもしれませんが、それが JavaScript ガベージ コレクターに関する私の理解の基礎です。

編集: これは、スクリプトのリーク バージョンの多くの可能性のうちの 1 つです。

<html>
<body>
</body>
<script>
var i, el;

var createdElements = {};
var events = [];

function attachAlert(element) {
    element.onclick = function() { alert(element.innerHTML); };
}

function reallyBadAttachAlert(element) {
    return function() { alert(element.innerHTML); };
}

for (i = 0; i < 1000000; i++) {
    el = document.createElement('div');
    el.innerHTML = i;

    /** posibility one: you're storing the element somewhere **/
    attachAlert(el);
    createdElements['div' + i] = el; 

    /** posibility two: you're storing the callbacks somewhere **/
    event = reallyBadAttachAlert(el);
    events.push(event);
    el.onclick = event;

}
</script>
</html>

したがって、#1 では、単にその要素への参照をどこかに格納しているだけです。決して使用しないことは問題ではありません。その参照はオブジェクトで行われるため、要素とそのコールバックは決して消えません (または、少なくともオブジェクトから要素を削除するまで)。可能性 2 については、イベントをどこかに保存している可能性があります。要素がどこにも見つからなくても、イベントにアクセスできる (つまり、 を実行することによってevents[10]();) ため、イベントによって引き続き参照されます。したがって、要素は、配列から削除されるまで、イベントと同様にメモリに残ります。

于 2013-04-27T20:25:33.643 に答える
1

2020 アップデート:

ほとんどの CPU 側のメモリ オーバーフローは、最新の v8 エンジン ベースのブラウザーでは機能しなくなりました。ただし、このスクリプトを実行すると、GPU 側のメモリがオーバーフローする可能性があります。

// Initialize canvas and its context
window.reallyFatCanvas = document.createElement('canvas');
let context = window.reallyFatCanvas.getContext('2d');

// References new context inside context, in loop.
function leakingLoop() {
    context.canvas.width = document.body.clientWidth;
    context.canvas.height = document.body.clientHeight;
    const newContext = document.createElement('canvas').getContext('2d');
    context.context = newContext;
    context.drawImage(newContext.canvas, 0, 0);
    
    // The new context will reference another context on the next loop
    context = newContext;
}

// Use interval instead of while(true) {...}
setInterval(leakingLoop,1);

編集:すべての変数(および定数)の名前を変更して、非常に理にかなっています。これが説明です。

私の観察によると、キャンバス コンテキストはビデオ メモリと同期しているようです。そのため、別のキャンバス オブジェクトなどを参照するキャンバス オブジェクトの参照を配置すると、ビデオ RAM は DRAM よりもはるかに多くなり、microsoft edge と chrome でテストされました。

これは、スクリーンショットの 3 回目の試みです。JavaScript による VRAM リーク

このスクリプトの実行中にスクリーンショットを撮った後、ラップトップが常に数秒フリーズする理由がわかりません。そのスクリプトを試してみたい場合は注意してください。

于 2020-08-04T17:30:02.663 に答える