web-dev-qa-db-ja.com

chrome dev toolsでJSメモリリークを見つける

私はchrome開発ツールを使用して、一部のJSコードにメモリリークがある場合に問題を解決しています。memory timelineは、メモリが期待どおりに再利用されているようです。

enter image description here

ただし、memory snapshotは、「切り離されたDOMツリー」の下にエントリがあるため、リークがあるように見えるため、混乱を招きます。

「切り離されたDOMツリー」の下にあるものは、ガベージコレクションが行われるのを待っているだけですか、それともこれらの実際のリークですか?

デタッチされた要素への参照を保持している関数を見つける方法を知っている人もいますか?

enter image description here

41
Carl Rippon

これらの要素はコードで参照されていますが、ページのメインDOMツリーから切断されています。

簡単な例:

var a = document.createElement("div");

aは切断された要素を参照するようになりました。aがまだスコープ内にある場合は、GCできません。

切り離されたdomツリーがメモリに残っている場合は、それらへの参照を保持しています。 jQueryでこれを行うのはやや簡単です。トラバースした結果への参照を保存し、それを保持するだけです。例えば:

var parents = $("span").parent("div");
$("span").remove();

これで、スパンを参照しているように見えない場合でも、スパンが参照されます。 parentsはjQueryを通じてすべてのスパンへの参照を間接的に保持します.prevObjectプロパティ。そうやってparents.prevObjectは、すべてのスパンを参照するオブジェクトを提供します。

ここの例を参照してください http://jsfiddle.net/C5xCR/6/ 。スパンが直接参照されているように見えない場合でも、実際にはparentsグローバル変数によって参照されているため、分離DOMツリーの1000スパンが消えることはありません。

これが同じjsfiddleですが、次のようになります。

delete parents.prevObject

そして、スパンがデタッチされたdomツリー、またはその問題のどこにもなくなっていることがわかります。 http://jsfiddle.net/C5xCR/7/

34
Esailija

「切り離されたDOMツリー」の下にあるものは、ガベージコレクションが行われるのを待っているだけですか、それともこれらの実際のリークですか?

スナップショットを作成する前に、ブラウザーはガベージコレクションを実行し、参照されていないすべてのオブジェクトをスイープします。したがって、ヒープスナップショットには常にライブオブジェクトのみが含まれます。その結果、分離されたDOMツリーがスナップショットにある場合、JavaScriptから参照されるツリー内の要素が存在している必要があります。

また、切り離された要素への参照を保持している関数を見つける方法を誰かが知っていますか?

同じ分離されたDOMツリーには、背景が黄色の要素(またはいくつか)が必要です。このような要素はJavaScriptコードから参照されます。誰が要素ツリーの参照を正確に保持しているのかを、保持ツリーで確認できます。

15

JQueryタグを追加したので、これがjQuerythingであるという卑劣な疑いがありました。簡単なグーグルは私に このページ をもたらしました。 jQのdetachメソッドを使用する場合、オブジェクトへの参照は引き続きメモリに保持されるため、スナップショットが発生する可能性があります。

もう1つは、jQueryがdivノードを手元に持っていることです。これは、明らかにメモリに保持されますが、実際のdomには決して追加されません... document.createNode('div')を追加せずに。これもメモリスナップショットに表示されます。これを回避することはできません。jQueryはこれを使用して文字列を解析してhtml要素にします。

メモリからいくつかの要素を削除するには、 jQuery .remove()メソッドを使用すると、memは即座に消去されますcf Esailijaがremoveがこの法案に完全に適合しない理由についてのコメント
$('#someElem')[0].parentNode.removeChild($('#someElem')[0]);要素を完全に削除する必要がありますが、イベントのバインドを解除しない場合があります。おそらく次のようなものがあります:

_$('#someElem').detach();//to remove any event listeners
$('#someElem')[0].parentNode.removeChild($('#someElem')[0]);//remove element all together
_

また、Esailijaが彼の回答で指摘したように、jQueryオブジェクト(var someRef= $('.someSelector');)への参照は、GCされないため、グローバル変数に割り当ててください。実際には、グローバルをすべて一緒にしないでください。
しかし、質問に簡潔かつ明確に答える必要があります。これらは実際のメモリリークではありません。メモリはonbeforeunloadで解放する必要がありますイベント。 jQueryオブジェクトが削除されるため、すべての参照がスコープ外になります。少なくとも、それが私の「研究」によって私を信じさせてくれます。 おそらく完全に関連しているわけではありませんが、参照としてのみ これは、私がしばらく前に投稿したmem-leaksに関する質問です。

1