08_JavaScript_Memory_Profiling
Table of Contents
- 1. JavaScript Memory Profiling JavaScript のメモリのプロファイリング (Last updated 2013-12-03)
- 1.1. 自身に尋ねる質問 (自問する質問) Questions to ask yourself
- 1.2. 用語や基礎 Terminology and Fundamentals
- 1.3. Prerequisites and helpful tips 前提条件と役立つヒント
- 1.4. Heap Profiler ヒーププロファイラ
- 1.5. Views in detail ビューの詳細
- 1.6. Object allocation tracker オブジェクト割り当てのトラッカー (追跡者)
- 1.7. Memory Profiling FAQ メモリのプロファイリングよくある質問
- 1.7.1. Q: オブジェクトのすべてのプロパティが表示されない、彼らの文字列以外の値が表示されない! なぜ?
- 1.7.2. Q: @ の文字の後の数字は何を意味する? – アドレスまたは ID ですか? ID 値は本当にユニークですか?
- 1.7.3. Q: 「死んだ」(到達不能)"dead" (unreachable) オブジェクトは、スナップショットに含まれていますか?
- 1.7.4. Q: GC のルートには何が含まれて (comprises) いますか?
- 1.7.5. Q: 私はメモリリークを検出するために Heap Profiler (ヒーププロファイラ) と Timeline Memory (タイムラインメモリ) ビューを使用するように言われてきた。どのツールを、最初に使用すべきですか?
- 1.7.6. Q: 私は、ヒープスナップショットで DOM ノードの数に気づきました。それは、一部は赤で強調 (ハイライト) され「独立している (デタッチ, Detached) DOM ツリー」として示されていた、一方で、他のものは黄色で。 これは何を意味しますか?
- 1.7.7. Q: Shallow Size (浅く) の列と Retained Size (保持された) の列 (カラム) は何を表わしますか。また、それらの間の違いは何ですか。
- 1.7.8. Q: constructor (コンストラクタ) と retained views (保持ビュー) に多くのデータがあります。 漏れがあるかどうか、発見するためにどこから調べ始めるべき?
- 1.7.9. Q: 異なる Summary (概要)、Comparison (比較)、Dominators (ドミネータ) と Containment (封じ込め) ビューの違いは何ですか?
- 1.7.10. Q: Heap (ヒープ) プロファイラのさまざまなコンストラクタ (グループ) エントリはどのように対応していますか?
- 1.7.11. Q: 図 (数字) に影響を及ぼしているかもしれないものとして、Chrome でオフする必要があることはありますか?
- 1.7.12. Closing remarks 結びの言葉
- 1.8. Supporting Demos サポートのデモ
- 1.9. Community Resources コミュニティリソース
1 JavaScript Memory Profiling JavaScript のメモリのプロファイリング (Last updated 2013-12-03)
memory leak (メモリーリーク) とは、利用可能なコンピュータ・メモリーの徐々に損失することです。 プログラムが、それが一時使用のために得たメモリを繰り返し返さない場合、それが生じます (occurs) 。 (プログラムが繰り返し、それが一時的に使用を得ていたメモリを返すことに失敗した場合に発生します。 ) JavaScriptのWebアプリは、多くの場合、ネイティブアプリケーションが行うことと同様のメモリ関連の問題に苦しむことになります、 このような leaks (リーク) や肥大化 (bloat) だけではなく、彼らはまた、garbage collection pauses (ガベージコレクションの休止) に対処する必要もあります。
JavaScript は自動メモリ管理のためのガベージコレクションを使用していますが、 効果的な (effective Effectively managing memory at Gmail scale - HTML5 Rocks ) メモリ管理は依然として重要である。 このガイドでは、JavaScript の Web アプリにメモリの問題のプロファイリングについてウォークスルーします。 ツールがどのように実際上作動するかのあなたの意識を改善するために、特徴に関して学習する場合、必ず supporting demos を試してみてください。
この文書で使用されている用語に慣れるために Memory 101 ( Memory Analysis 101 - Chrome DevTools — Google Developers ) ページをお読みください。
注:我々が使用するこれらの機能のいくつかは、現在、Chrome Canary のみ利用可能です。 アプリケーションのための最高のメモリのプロファイリング·ツールを取得するには、このバージョンを使用することをお勧めします。
1.1 自身に尋ねる質問 (自問する質問) Questions to ask yourself
一般に、メモリーリークを持っていると思う場合、答えることになるだろう 3 つの疑問がある :
- 私のページはあまりにも多くのメモリを使用していますか?
Timeline memory view (Timeline メモリビュー) および Chrome task manager (Chrome タスクマネージャー) は、 あまりにも多くのメモリを使用していれば、あなたが特定するのに役立ちます。 メモリビューは、レンダリングする検査過程でのライブ DOM ノード、文書やJSのイベント·リスナーの数を追跡することができます。 メモリビューは、検査されたレンダリングプロセスの中の、実際の DOM ノード、ドキュメントおよび JS イベントリスナーの数を追跡することができます。
経験則として: もはや使用する必要のない DOM 要素への参照を保持しないようにして、 無用のイベントリスナーをアンバインドして、 使用するつもりでない (日付 -> date ではなくて data かも???) の大きな塊を格納する場合、気を付けてください。
- 私のページはメモリーリークがないか?
Object allocation tracker (オブジェクト割り当てトラッカー) を使用すると、リアルタイムでのJSオブジェクトの割り当てを見て、リークを絞り込むことができます。 さらに、JS ヒープスナップショットをとり、メモリ・グラフを分析し、 どのオブジェクトがガベージ・コレクションによって清掃されていないか発見するスナップショットを比較するために heap profiler を使用することができます。
- 私のページは、どれくらい頻繁にガベージ・コレクションを強要していますか?
頻繁 (frequently) GCing している場合は、あまりにも頻繁に割り当てることができる。 Timeline メモリビューでは、興味のあるポーズを特定するのに役立ちます。
1.2 用語や基礎 Terminology and Fundamentals
このセクションでは、memory analysis (メモリ分析) で使用される一般的な用語を説明し、異なる言語のためのメモリプロファイリングツールの様々に適用可能である。 ここで記述された用語と概念は、ヒープ・プロフィーラー UI および対応するドキュメンテーションの中で使用されます。
それは、ツールを有効に使用するためにこれらに精通するのを支援します。 あなたがこれまでの Java、 .NET、またはいくつかの他のメモリプロファイラのどちらかを使ってきた場合、これ以降には補習になってしまう内容があります。
1.2.1 オブジェクトサイズ Object sizes
メモリを原始的 (primitive) なタイプ (数とストリングのような) および オブジェクト (連想配列) を備えたグラフと見なしてください。 それは、相互接続されたポイントを備えたグラフとして視覚的に以下のように表わされるかもしれません :
オブジェクトは、次の 2 つの方法でメモリを保持することができます :
- オブジェクト自体によって直接
- 他のオブジェクトへの参照を保持し、それらのオブジェクトが ガベージコレクタ (略してGC) によって自動的に配置されるのを防ぐ (したがって) ことにより暗黙に。
DevTools (「Profiles」の下で見つかったメモリ問題を調査するためのツール ) の中の Heap Profiler が働く時、 恐らくあなた自身が情報の少数の異なるカラムを見ているのを見つけるでしょう。 2つ、それは目立つ、Shallow Size (浅いサイズ) および Retained Size (保持されたサイズ) はありますか。しかし、これらは何を表わすでしょう?
- 浅いサイズ Shallow size
これはオブジェクト自体によって保持されるメモリのサイズです。
典型的な JavaScript オブジェクトは、それらの記述 (説明) および即時の値の格納のためにいくつかのメモリを確保しておきます。 通常、配列および文字列だけが重要な shallow サイズを持つことができます。 しかしながら、文字列および外部配列は、 JavaScript heep の上の小さな ラッパーオブジェクトだけを公開して、レンダラメモリでのそれらの主記憶装置をしばしば持っています。
検査された (inspected) ページが与えられる場合、レンダラメモリ (Renderer memory) はすべてプロセスのメモリである : ネイティブ·メモリー + ページの JS ヒープメモリ + ページ単位で始められたすべての専用の (dedicated) ワーカーのJSヒープメモリ しかしながら、小さなオブジェクトさえ、他のオブジェクトが自動的なガベージ・コレクション・プロセスによって処分されるのを防ぐことにより、 間接的に大量のメモリを保持することができます。
- 保持されたサイズ Retained size
これは、一旦オブジェクトがそれ自身 GC roots (GC のルート) から到達不能に (手が届かなく) なったその依存 (dependent) オブジェクトと共に削除されれば、 解放されるメモリのサイズです。
GC roots (GCのルート) は、V8 の外側のネイティブコードから JavaScript オブジェクトまで参照する場合、 作成される (ローカルまたはグローバルのどちらか) ハンドルから構成されます。 そのようなハンドルはすべて、GC roots > Handle scope と GC roots > Global handles 下のヒープスナップショット内に見つけることができる。 このドキュメンテーションにブラウザの実装の詳細に飛び込まずに、ハンドルについて記述することは混乱させるかもしれません。 GCのルートおよびハンドルの両方は心配する必要のあるものではありません。
大部分がユーザにとって面白くない、多くの内部 GC のルートがあります。 アプリケーションの観点から見ると、次の種類のルートがあります :
- (各 iframe 内の) ウィンドウグローバルオブジェクト。 ヒープスナップショットにウィンドウからの最短保持パス上のプロパティ参照の数である距離 (distance) フィールドがある。
- ドキュメントの横断により到達可能なすべてのネイティブの DOM ノードから成るドキュメントの DOM ツリー。 それらのすべてには JS ラッパーがあるとは限らないかもしれません、しかし、それらが持っていれば、ラッパーは生きているでしょう。その一方でドキュメントは生きています。
- 時々、オブジェクトはデバッガ・コンテキストおよび DevTools コンソール (例えばコンソール評価の後の) によって保持されるかもしれません。
注 : 私たちは、明瞭なコンソールおよびデバッガ中の非アクティブ (無活発) なブレークポイントをスナップショットに積み上げるようにユーザに勧めます。
メモリ・グラフはルートで始まります。それは、ブラウザの window オブジェクトあるいは Node.js モジュールの Global オブジェクトかもしれません。 このルートオブジェクトがどのようにGC'dかコントロールしません。
ルートから到達できないものは何でも、GCを取得。
1.2.2 Object's Retaining Tree オブジェクトのリテーニング (保持) ツリー
私たちが以前に紹介したように、ヒープは相互接続された (interconnected) オブジェクトのネットワークです。 数学的な世界では、この構造はグラフまたはメモリのグラフと呼ばれます。 グラフは端 (エッジ) によって接続しているノードで構成 (構築) されます。それらの両方はラベルを与えられています。
- Nodes (or objects) は、それらを構築するために使用されたコンストラクタ関数名を用いて標識される
- Edges は、プロパティの名前を用いて標識される。
このガイドでは、ヒーププロファイラを使用してプロファイルを記録する方法を学習します。 我々は以下のヒーププロファイラ記録 (recording) で見ることができる目立つもののうちいくつかは、距離を含んでいます。 : GC のルートからの距離。 同じタイプのほとんどすべてのオブジェクトが同じ距離にあり、少数がより大きな距離にある場合、それは調査する価値があるものです。
1.2.3 Dominators ドミネータ, 支配者
各オブジェクトは、正確に一つの支配者を有するので、支配オブジェクトは、ツリー構造で構成されてます。 オブジェクトの支配者は、それが支配するオブジェクトへの直接参照が不足していることがあります。すなわち、ドミネータ (支配者) ツリーはグラフのスパニングツリーではありません。
上図 (In the diagram above) :
- ノード 1 は、ノード 2 を支配
- ノード 2 は、ノード 3, 4, 6 を支配
- ノード 3 は、ノード 5 を支配
- ノード 5 は、ノード 8 を支配
- ノード 6 は、ノード 7 を支配
下記の例において、ノード #3 は #10 の支配者である。しかし、#7 はさらに GC から #10 まですべての単純なパスの中で存在する。 したがって、object B がルートから object A まですべての単純なパスの中で存在する場合、 B は A の支配者である。
1.2.4 V8 Specifics V8 の詳細
このセクションでは、私たちは、特に V8 JavaScript virtual machine (V8 VM あるいは VM) に相当するいくつかのメモリ関連のトピックについて記述します。 メモリのプロファイリング (輪郭を書く) するとき、なぜヒープスナップショットがこの方法を見るか理解することは有用です。
- JavaScript Object Representation JavaScript のオブジェクトの表記
3 つのプリミティブ (原始) のタイプがあります。 :
- Numbers 数字 (e.g3.14159…)
- Booleans ブール (true または false)
- Strings 文字列 (e.g 'Werner Heisenberg')
彼らは、他の値を参照することはできず、常に葉または終端ノードです。
- Numbers
数値はどちらかとして格納することができます :
- 即時 31 ビット整数は、小さな整数(SMIの)と呼ばれる値、または
- ヒープ番号と呼ばれた、ヒープオブジェクト
値は、それにプロパティを設定するように、ボックス化する必要がある場合にヒープ番号は、 ??? 例えばダブルスとしてSMI形に適合しない値を格納するために使用される、またはされている。 ???
- Strings
文字列はどちらかに格納することができます :
- VM のヒープ、または
- 外部的な renderer’s memory (レンダラのメモリ) に。 ラッパーオブジェクト (wrapper object) が作成され、たとえば、Web から受信されたスクリプトのソース、およびその他のコンテンツが格納されているのではなく、 VM ヒープにコピーし、外部記憶装置にアクセスするために使用される。
新しい JavaScript オブジェクトのためのメモリは、専用の JavaScript のヒープ (または VM ヒープ) から割り当てられます。 これらのオブジェクトは V8 のガベージコレクターによって管理され、それらへの少なくとも 1 つの強い参照がある限り、したがって、生き続ける (stay alive) でしょう。
- Native objects ネイティブオブジェクト
ネイティブオブジェクトは、JavaScript のヒープにない他のものすべてです。 ネイティブオブジェクトは、それが生涯の全体 (throughout) であるヒープオブジェクトとは対照的で、 V8 ガベージコレクタによって管理されておらず、JavaScript ラッパーオブジェクトを使用する JavaScript からのみアクセスすることができます。
- Cons string 定数 ???
定数 (???) は、連結されて、そのとき格納された文字列のペアで構成され、連結の結果であるオブジェクトです。 Cons string の内容の結合が必要な場合にのみ発生します。 結合した文字列の部分文字列を構築する必要がある場合の例は次のようになります。 ???
例えば、連結すれば、a そして b、連結の結果を表わすストリング (a, b) を得ます。 その後その結果と d を連結したならば、別の cons string ((a, b), d) を得ます。
- Arrays 配列
配列は、数字キーを備えたオブジェクトです。 それらは、大量のデータの格納のために V8 VM の中で広範囲に使用されます。 辞書のように使用されるキーと値のペアのセットは配列によってバックアップされます。
典型的な JavaScript オブジェクトを格納するために使用される 2 つの配列型のいずれかになります。 :
- 命名されたプロパティ、および
- 数値要素 (numeric elements)
プロパティが非常に数が少ない場合には、それらは、JavaScript オブジェクト自体に内部的に格納することができる。
- Map
オブジェクトとそのレイアウトの種類について記述するオブジェクト。 例えば、マップは、高速にプロパティにアクセス ( fast property access ) するための暗黙的オブジェクト階層を記述するために使用される。
- Object Groups オブジェクトグループ
各ネイティブオブジェクトグループはお互いに相互参照を保持するオブジェクトから構成されています。 このように接続されたグラフを形成し、例えば、すべてのノードがその親と次の子と次の兄弟へのリンクへのリンクを持つ DOM サブツリーを考えてみましょう。 ネイティブオブジェクトは、JavaScript のヒープに示されていないことに注意してください - 彼らはゼロサイズを持っている理由です。 その代わりに、ラッパーオブジェクトが作成されます。
各ラッパーオブジェクトは、それにコマンドをリダイレクトするため、対応するネイティブオブジェクトへの参照を保持します。 自身のターンでは、オブジェクトグループは、ラッパーオブジェクトを保持する。 しかしながら、GC はそのラッパーもはや参照されないオブジェクトグループを解放するのに十分スマートなように、これは回収不能のサイクルを作成しません。 しかし、単一のラッパーを解放するのを忘れることは、グループ全体、及び、関連するラッパーを保持するでしょう。
1.3 Prerequisites and helpful tips 前提条件と役立つヒント
1.3.1 Chrome Task Manager Chrome タスクマネージャ
注: Chrome でメモリの問題をプロファイリングするときは、クリーンルームのテスト環境 (clean-room testing environment) をセットアップすることをお勧めします。
Chrome タスクマネージャーを使用すると、これを起こらせているかもしれないアクションを行なう間にメモリカラムをモニターすることにより、 ページが多くのメモリを消費しているかどうか速く確かめることができる。 タスクマネージャは、[Chrome メニュー] > [ツール] から、あるいは、 [Shift + Esc] キーを押してアクセスされます。
図注釈 : A simple app, but it's using a lot more memory than expected. This might prompt further invesigation. シンプルなアプリですが、予想よりはるかに多くのメモリを使用しています。これはさらなる調査を要求される場合があります。
オープン後は、列の見出し領域を右クリックして、JavaScriptのメモリ列を有効にしてください。
1.3.2 Identifying a Memory Problem with the DevTools Timeline DevTools タイムラインとメモリの問題の識別
パフォーマンスの問題を解決する最初のステップは、問題が存在することの証明を示す能力を持っていることです。 これは問題のベースライン測定を行うために使用することができる再現可能なテストを作成することができることを意味する。 再現性のあるプログラムがなければ、確実に問題を測定することはできません。 さらに、基線測定なしでは、作られたどんな変更もパフォーマンスを改善していることを知る方法はありません。
Timeline パネルは、問題がいつ存在するか決定するのに役立ちます。 それは、あなたの Web アプリケーションかページでロードし対話する場合、時間がどこで費やされるかの完全な概要を提示します。 すべてのイベント、 リソースのロードから JavaScript の構文解析まで、 スタイルの算出、 ガベージコレクションによる休止、 再描画が、タイムライン上にプロットされています。
メモリの問題を確認する場合は、タイムラインパネルの Memory view (メモリビュー) は、追跡のために使用することができます :
- 総割り当てられたメモリを - メモリ使用量が増加している?
- DOM ノードの数
- ドキュメントの数と
- イベントリスナーの数が割り当てられた (allocated) 。
あなたのメモリプロファイリングセッション中にリークを引き起こしている可能性のある問題を特定する方法の詳細を読むには、 Memory profiling with the Chrome DevTools by Zack Grossbart を参照
1.3.3 Proving a Problem Exists 問題の存在の証明
最初にすることはメモリをリークしている疑いがある (suspect) アクションのシーケンスを識別することです。 これは、サイトをナビゲート, ホバーする, クリックする、 より多くのパフォーマンスにマイナスな時間に渡って影響を与えているように見える方法で そうでなければなんとかしてページと対話することからの何でもでありえます。
Timeline パネル上 ( [Ctrl + E] または [Cmd + E] ) で記録を開始し、テストする一連の (sequence) アクションを実行します。 十分なガベージコレクションを強制するには、下部のゴミ箱アイコン (trash icon) をクリックしてください。
以下に、私たちはメモリーリーク・パターンを見ます。なおここで、いくつかのノードは集められていません :
数回の反復した後に、(上部の memory pane (メモリペイン) で) ノコギリ形のグラフが表示された場合は、 すぐに住んでいたオブジェクト (shortly lived objects) の多くを割り当てている (allocating) 。 アクションのシーケンス(一連のアクション)が、任意の保持されたメモリに帰着するとは予想されず、 あなたが始めたところで、DOM ノードカウントがベースラインまで低下しない場合、漏れ (leak) があるのではないかと疑問に思う十分な理由があります。
一旦その問題が存在することを確認したならば、Profiles パネル上でヒーププロフィーラーを使用して、その問題の原因を特定する助けを得ることができます。
例: あなたが効果的に Timeline メモリモードの使用方法を練習することができ、 memory growth (E 1) (メモリの増加)のこの例を試してみてください。
1.3.4 Garbage Collection ガーベジコレクション
garbage collector ガベージコレクタ (V8 の中のもののような) は、実際のあなたの適用でのオブジェクトを見つけることができる必要がある、と同様に、死んでいる (ごみ) と考えられ、unreachable (到達不能) なもの。
garbage collection ガベージコレクション (GC) があなたの JavaScript 中の論理エラーにより、どんな死んでいるオブジェクトも逃す場合、 これらのオブジェクトによって消費するメモリは取り戻すことができません (再利用することができません) 。 このような状況は、時間の経過とともに、アプリケーションを遅くしてしまいます。
必要としない変数、および、イベントリスナーがあるコードによってまだ参照されているような方法で、あなたがコードを書いた場合、これはしばしば起こります。 これらの参照を維持したままでは、オブジェクトが正しく、GC によってクリーンアップすることができません。
あなたのアプリのライフサイクル中に更新されているかもしれない/破壊された DOM 要素への参照を含んでいる変数を、忘れずにチェックし無効にします。 他のオブジェクト (あるいは他の DOM 要素) を参照できるオブジェクトのプロパティをチェックしてください。 時間の経過とともに蓄積する可能性がある可変のキャッシュから必ず目を離さないでください。
1.4 Heap Profiler ヒーププロファイラ
1.4.1 Taking a snapshot スナップショットを作成
Profiles パネルで、Take Heap Snapshot (ヒープのスナップショットを取る) を選択し、 [Start (開始)]を押すか、 [Cmd + E] または [Ctrl + E] をクリックします。 :
スナップショットは、レンダラープロセスメモリ に最初に格納されます。 あなたがそれを表示するのにスナップショットのアイコンをクリックすると、それらはオンデマンドで DevTools に転送されます。 スナップショットが DevTools にロードされ、解析された後、スナップショットのタイトルの下に数が現われて、 到達可能な (reachable) JavaScript オブジェクトのサイズの合計を示します :
例: garbage collection in action (E 2) (アクションでのガベージコレクション) のこの例を試してみて、タイムラインでのメモリ使用量を監視します。
1.4.2 Clearing snapshots スナップショットをクリア
スナップ写真は (DevTools とレンダラメモリの両方から) Clear all profiles アイコン (禁止マークのようなアイコン) を押すことにより削除することができます :
注意: DevTools ウィンドウを閉じても、レンダラメモリから収集されたプロファイルを削除しません。 DevToolsを再開すると、すべての以前に記録したスナップショットは、スナップショットのリストに再表示されます。
我々は以前に、あなたのスナップショットワークフローの一部として DevTools から、GC を強制することができます述べたことを思い出してください。 ヒープスナップショットを作成すると、それは自動的に強制されます。 Timeline では、ごみ箱 (ごみの収集) ボタンをクリックすることにより、GC を強制することは非常に便利かもしれません。
例: scattered objects (E 3) (散在したオブジェクト) のこの例を試みて、ヒーププロフィーラーを使用して、それをプロファイリングしてください。 多くのアイテム (オブジェクト) の割付けが表示されるはずです。
1.4.3 Switching between snapshot views スナップショットビューを切り替える
スナップショットは、異なるタスクのために別の観点から見ることができます。 ビュー間で切り替えるためには、ビューの下部のセレクターを使用してください :
3 つのデフォルトのビューがあります :
- Summary 概要 - コンストラクタ名でグループ化されたオブジェクトを示しています。
- Comparison 比較 - 2スナップショット間の差分を表示します。
- Containment 封じ込め - ヒープの内容の調査を可能にします。
Dominators ビュー、 [Settings (設定)] パネルで有効にすることができる – dominators tree を示します。 蓄積 (accumulation) ポイントを見つけるのに役立ちます。
1.4.4 Looking up color coding カラーコードを調べる
オブジェクトのプロパティおよびプロパティの値は、異なるタイプを持っており、それに応じて着色されている。 各プロパティは、4 つのタイプのいずれかになります :
- a: property — 名前を持つ規則的 (regular) プロパティー、. (ドット) 演算子、または [] (大カッコ) 表記を介してアクセス可能。e.g. ["foo bar"];
- 0: element — 数値インデックスとの 正規 (regular) なプロパティー、[](大カッコ)表記を介してアクセス可能。
- a: context var — 関数コンテキスト内の変数、 関数クロージャ内からその名前でアクセス可能。
- a: system prop — JavaScript の VM によって追加されたプロパティ、JavaScript コードからのアクセス不可。
System として指定されたオブジェクトには対応する JavaScript のタイプ (型) を持っていません。 それらは、JavaScript の VM のオブジェクトシステム実装 (implementation) の一部である。 V8 は、ユーザの JS オブジェクトと同じヒープ内のほとんどのその内部オブジェクトを割り付けます。 したがって、これらは単に V8 内部です。
1.5 Views in detail ビューの詳細
1.5.1 Summary view 概要ビュー
最初に、スナップショットはオブジェクトの合計を表示する、概要 (Summary) ビューで開く。それは実例 (instances) を示すために拡張することができる :
トップレベルのエントリーはラインの「合計 (総数) 」です。 それらは次のものを表示します :
- Constructor represents 「コンストラクタを表す」は、このコンストラクタを使用して作成されたすべてのオブジェクトを表します
- number of object instances 「オブジェクトインスタンスの数」は、#列に表示されます
- Shallow size 「シャローサイズ」欄には、特定のコンストラクタ関数によって作成されたすべてのオブジェクトのシャローサイズの合計を表示します
- Retained size 「保持サイズ」欄には、オブジェクトの同じセットの中で最大保持サイズが表示されます
- Distance 「距離」は、ノードの最短の単純なパスを使用して、ルートまでの距離を表示します。
上図では、全行を展開した後、そのすべてのインスタンスが表示されます。 インスタンスごとの、シャローおよび保持サイズは、対応する列に表示されます。 @ 文字の後の数字は、オブジェクトの一意の ID である、 あなたはオブジェクトごとにヒープスナップショットを比較することができます。
例:サマリビューの使用方法を理解するには、この demo page (新規タブで開きます)をお試しください。
黄色のオブジェクトが、それらの上に JavaScript 参照を持ち、 ??? 赤色オブジェクトが、黄色の背景に 1 から参照されているノードを分離することを覚えておいてください。 ???
1.5.2 Comparison view 比較ビュー
このビューは、その差が漏洩したオブジェクトを見つけるためにあるものを見ることができるように、 複数のスナップショットを互いと比較するために使用される。 特定のアプリケーションの動作が (例えば、文書を開きそれを閉じるなどのように、通常ペアになる直接的かつ逆の操作などで任意のゴミを残してはいけません) リークを作ることがないことを確認するには、以下のシナリオに従うことができる。 :
- 操作を実行する前に、ヒープのスナップショットを取る。
- 操作を実行する (あなたがリークの原因であると考えているいくつかの方法でページを操作);
- 逆の操作を実行する (反対の相互作用をして、それを数回繰り返す);
- 2 番目のヒープのスナップショットを取り、この表示を [Comparison (比較) ] に変更、スナップショット 1 と比較する。
Comparison (比較) ビューでは、2 枚のスナップショット間の差が表示されます。 総エントリを拡張する場合は、追加され、削除されたオブジェクトインスタンスが示されています。
例:リークを検出するためのスナップショット比較を使用する方法のアイデアを得るために、この demo page (新規タブで開きます) をお試しください。
1.5.3 Containment view 封じ込めビュー
封じ込めビューは、基本的にアプリケーションのオブジェクト構造の「鳥瞰図」である。 それはあなたが一緒にあなたの JavaScript オブジェクトを構成している VM の内部オブジェクトを観察するために、 関数クロージャーの内側に覗くようにし、アプリケーションが非常に低いレベルで使用するメモリ量を理解することができます。
ビューは、複数のエントリポイントを提供します。 :
- DOMWindow objects – これらは、JavaScript コードのための「グローバル」オブジェクトとみなすオブジェクトです。
- GC roots — VMのガベージコレクタによって使用される実際のGCのルート;
- Native objects — 自動化を可能にするために JavaScript の仮想マシン内で「プッシュ」されているブラウザのオブジェクト、 例えば DOM ノード、CSS ルール(詳細は次のセクションを参照してください。)
下記は占められた (populated) Containment (封じ込め) ビューの例です :
例:ビューを使用して閉鎖やイベントハンドラを探索する方法を見つけるためにこの demo page (新規タブで開きます)をお試しください。
- A tip about closures クロージャーに関するヒント
それは、あなたが容易にスナップショット中のクロージャを識別することができるように、関数を指定するのを非常に支援する。 たとえば、この例では、名前付き関数を使用していません。 :
function createLargeClosure() { var largeStr = new Array(1000000).join('x'); var lC = function() { // this is NOT a named function return largeStr; }; return lC; }
一方、この例は行う :
function createLargeClosure() { var largeStr = new Array(1000000).join('x'); var lC = function lC() { // this IS a named function return largeStr; }; return lC; }
例: なぜ eval がメモリへのクロージャの影響を分析するのに有害か why eval is evil (E 7)、この例を試みてください。 さらに、 heap allocations (E 8) (ヒープ割付け) の記録に介して表示されるこの例でフォローアップに興味を持っているかもしれません。
1.5.4 Uncovering DOM leaks DOMリークを暴く
ツールのユニークな機能は、 ブラウザのネイティブオブジェクト (DOMノード、CSSルール) と、JavaScriptオブジェクト間の双方向の依存関係を反映することです。 これは、まわりに浮かぶ、忘れられた離れている (デタッチド, detached) DOM サブツリーにより何か起こりそうで目に見えないリークを発見するのに役立ちます。
DOM リークはあなたが思うよりも大きくなることがあります。 次のサンプルを考慮してください。 – #tree はいつ GC されるか?
var select = document.querySelector; var treeRef = select("#tree"); var leafRef = select("#leaf"); var body = select("body"); body.removeChild(treeRef); //#tree can't be GC yet due to treeRef treeRef = null; //#tree can't be GC yet due to indirect //reference from leafRef leafRef = null; //#NOW can be #tree GC
#leaf は、に、それの親 (parentNode) で、そしてまた #tree まで再帰的に、参照を維持します、 そのため、leafRef が無効にされる場合に限り、 #tree の下のツリー "全体" が GC に対する候補になる。
例: DOM ノードがどこに漏れる場合があるか理解する、leaking DOM nodes (E 6) それらを検知する方法をこの例を試みてください。 DOM leaks being bigger than expected (E 9) 予想以上に大きい DOM 漏れのこの例をさらに見ることにより、それをフォローアップすることができます。
参照: 詳細情報は、DOMリークやメモリ解析の基礎のチェックアウト詳細情報は、 Finding and debugging memory leaks with the Chrome DevTools by Gonzalo Ruiz de Villa.
ネイティブオブジェクトは、概要 (Summary) と封じ込め (Containment) ビューから最も簡単にアクセスできます - 専用のエントリーノードが彼らのためにあります。 :
例: 独立した DOM ツリーを再生するには、このデモ Uncovering DOM Leaks (新しいタブで開きます) をお試しください。
1.5.5 Dominators view ドミネータ (支配者) ビュー
ドミネータビューは、ヒープグラフのドミネータツリーを示しています。 ドミネータビューは、Containment (包含) ビューに似ていますが、プロパティ名を欠いている。 オブジェクトの支配は、それへの直接参照が不足している可能性があるためである、 つまり、ドミネータ木は、グラフの全域木 (スパニングツリー, spanning tree) ではありません。 しかし、すぐにメモリ蓄積ポイントを識別するために、私たちを助けるので、利益があります。
注意: Chrome Canary で、Dominators ビュー、 [設定] > [Show advanced heap snapshot] のプロパティを有効にし、に移動し、 DevTools を再起動することで有効にすることができます。
例: 蓄積ポイントを見つけることを訓練するために、このデモ Finding Accumulation Points (新しいタブで開きます)をしてみてください。 retaining paths and dominators (E 10) パスとドミネータを保持に実行しているの、この例でそれをフォローアップ。
1.6 Object allocation tracker オブジェクト割り当てのトラッカー (追跡者)
object tracker (オブジェクトトラッカー) は、インクリメント (増分) の更新および Timeline パネルのトラッキングと、ヒーププロフィーラー heap profiler の詳細なスナップショット情報を組み合わせる。 これらのツールと同様に、トラッキング (追跡) オブジェクトのヒープ割り当ては、一連のアクションを実行して、記録を開始することを含む、その後、分析のために記録を停止する。
オブジェクトトラッカーは、録音の終わりに録音 (50ms ごとと同じくらい頻繁に!) 、および、1 枚の最終スナップショットの全体にわたってヒープスナップショットを周期的 (定期的) にとります。 ヒープ割り当てプロファイルは、オブジェクトがどこで作成されているか示し、保持するパスを識別します。
1.6.1 Enabling and using the Object Tracker オブジェクトトラッカー (追跡) の有効化と使用
オブジェクトトラッカーの使用を開始するには :
- 最新の Chrome Canary であることを確認してください。
- Developer Tools を開いて、右下の歯車 (gear) アイコンをクリックしてください。
- Profiler パネルを開くには、「Record Heap Allocations (レコードヒープ割り当て)」 というプロファイルが表示されます
新しいオブジェクトがヒープ内で発見された場合に、上部のバー (bars) に示される。 各バーの高さは、最近割り当てられたオブジェクトのサイズに対応し、 バーの色は、これらのオブジェクトが依然として最終的なヒープスナップショットに生きているかどうかを示します。 : 青い棒は、スケジュールの終わりにまだ生きているオブジェクトを示します、 灰色の棒は、スケジュール中に割り付けられたが、それ以来ガベージコレクトされたオブジェクトを示します。
上記の例では、アクションは 10 回行った。 サンプルプログラムでは、5つのオブジェクトをキャッシュするため、したがって、最後の 5 本の青いバーが期待されている。 しかし、一番左の青いバーは、潜在的な問題を示しています。 その後、その特定のスナップショットにズームインし、かつ、その時点で最近割り当てられたオブジェクトを表示するために 上記のタイムラインでスライダを使用することができます。
ヒープ内の特定のオブジェクトをクリックすると、ヒープのスナップショットの下部に、その保持するツリーが表示されます。
オブジェクトへの保持パスの検査は、あなたのオブジェクトが収集されなかった理由を理解するのに十分な情報を与える必要があり、 また、不要な参照を削除するために必要なコードの変更を行うことができます。
1.7 Memory Profiling FAQ メモリのプロファイリングよくある質問
1.7.1 Q: オブジェクトのすべてのプロパティが表示されない、彼らの文字列以外の値が表示されない! なぜ?
すべてのプロパティは、JavaScript ヒープの上に実際に格納されるとは限りません。 それらのいくつかは、ネイティブコードを実行するゲッターを使用して実装される (implemented) 。 そのような特性はゲッター (getters) を呼ぶコストを回避し、かつゲッターが "純粋な (pure) " 機能でない場合に、 可能な (possible) プログラム状態の変更を回避するために、ヒープスナップ写真の中で捕らえられません。 さらに、数値などの非ストリング(文字列)値は、スナップショットのサイズを削減する目的で捕獲されません。
1.7.2 Q: @ の文字の後の数字は何を意味する? – アドレスまたは ID ですか? ID 値は本当にユニークですか?
これはオブジェクト ID です。 オブジェクトがガベージコレクション中に移動されるので、オブジェクトのアドレスの表示は意味をなさない。 これらのオブジェクト ID は、実際の ID です – その手段は、それらは得られた多数のスナップショット中で固執 (持続, persist) し、ユニークです。 これは、ヒープ状態間の正確な比較を可能にします。 これらの ID を維持することは、GC のサイクルにオーバーヘッドが追加されますが、 最初のヒープスナップショットが得られた後にのみ、開始されます – ヒーププロファイルが使用されない場合、オーバーヘッドはありません
1.7.3 Q: 「死んだ」(到達不能)"dead" (unreachable) オブジェクトは、スナップショットに含まれていますか?
いいえ。 到達可能なオブジェクトだけがスナップショットに含まれています。 さらに、スナップショットをとることは、常に GC を行うことから始まります。
注: 執筆時点で、私たちは、ヒープのスナップショットを撮影するときに使用するヒープサイズの低下を軽減するために、この GC を避けることを計画している。 これはまだインプリメントされて (実装されて, implemented) いません。しかし、ごみは、まだスナップショットの外でしょう。
1.7.4 Q: GC のルートには何が含まれて (comprises) いますか?
多くのこと :
- built-in object maps; 内蔵されたオブジェクトマップ
- symbol table; シンボルテーブル
- stacks of VM threads; VM のスレッドのスタック
- compilation cache; コンパイルキャッシュ
- handle scopes; スコープを扱う
- global handles; グローバルハンドル
1.7.5 Q: 私はメモリリークを検出するために Heap Profiler (ヒーププロファイラ) と Timeline Memory (タイムラインメモリ) ビューを使用するように言われてきた。どのツールを、最初に使用すべきですか?
Timeline 。 あなたがページを長時間使用した後に鈍化していることに最初に気づくとき、過度のメモリ使用量を診断するためにそれを使用してください。 減速は以前メモリーリークの古典的な症状でした。しかし、それはさらに別のものかもしれない。 – おそらく、ページ内で paint やネットワークのボトルネックを持っています。ですから、ページで実際の問題を解決することを確実にしてください。
メモリが問題であるかどうかを診断するには、Timeline パネルと Memory ビューに移動します。 レコードボタンを押して、アプリケーションと対話して、漏れを引き起こしているかもしれないと思うあらゆるステップを繰り返します。 録音を止めてください。 グラフには、アプリケーションに割り当てられたメモリが表示されます。 それがもし時間にわたって (常に落ちることのない) 増加する量のこれを消費していたら、それは、メモリーリークしているかもしれない表示です。
メモリは、割り付けられたガベージコレクタが来た時に解放されているように、健康的なアプリケーションのプロファイルは、より多くの鋸 (のこぎり) 歯状の曲線のようになります。 ここで心配することは何もない – そこには常に JavaScript 、および、空の requestAnimationFrame さえ中で取引するコストが常にあるでしょう、このタイプのノコギリの原因となります、あなたはそれを避けることはできません。 それが多くの割付け (配分, allocations) が作られている表示であるのでそれが鋭くない、(それは反対側の多くのごみと一致することができます) ことをただ確認してください。
目を離さないようにする必要があるのは、この曲線の勾配の増加率です。 DOM ノードカウンタ、Document (文書) カウンタ、 診断中に役立つ Memory ビュー中の Event (イベント) リスナーをカウント、があります。 DOM ノードは、ネイティブメモリーを使用し、直接 JavaScript のメモリグラフには影響しません。
メモリリークを持っている疑いがあるなら、Heap (ヒープ) プロファイラは、漏れの原因を発見するために使用することができます。
1.7.6 Q: 私は、ヒープスナップショットで DOM ノードの数に気づきました。それは、一部は赤で強調 (ハイライト) され「独立している (デタッチ, Detached) DOM ツリー」として示されていた、一方で、他のものは黄色で。 これは何を意味しますか?
あなたは、いくつかの異なる色のノードに気付くでしょう。 赤いノード (暗い背景) は、JavaScript からそれらまで直接参照がありませんが、それらは切り離された DOM ツリーの一部であるので生きています。 JavaScript (恐らく閉鎖か変数としての) からの参照がツリーのノードがあるかもしれません、それが偶然に DOM ツリー全体がガベージコレクトされるのを防いでいるのかもしれません。
しかしながら、黄色のノード (黄色の背景を備えた) は、JavaScript から直接に関係があります (直接参照を持っている) 。 あなたの JavaScript からの参照を特定するために同じ デタッチ (離れている) DOM ツリーの黄色のノードを探してください。 DOM ウィンドウから要素に結びつく一続きの (chain) プロパティがあるにはずです。 (e.g window.foo.bar1.baz)
離れているノードが全体像にどこに入るかのアニメーションは、下のように見ることができます :
例: デタッチノードを検索するヒープのスナップショットを取るし、タイムライン内のノードの進化を見ることができる detached nodes (E 4) のこの例を試してみてください。
1.7.7 Q: Shallow Size (浅く) の列と Retained Size (保持された) の列 (カラム) は何を表わしますか。また、それらの間の違いは何ですか。
したがって、オブジェクトは、2 つの異なる方法で (生きている) メモリに保持することができます。 – 直接、別の生きているオブジェクトのいずれかによって (ウィンドウと文書は常に生きているオブジェクトです) または、暗黙的に (DOM オブジェクトのように) レンダラーのネイティブ部分からの参照を保持することによって。
後者は、これらのオブジェクトが GC によって自動的に配置されるのを防止してしまうもので、その結果リークを引き起こすものである。 オブジェクト自身が保持しているメモリのサイズはシャロー (shallow) サイズとして知られている (一般的には、配列や文字列は、より大きなシャローサイズを持つ) 。
任意のサイズのオブジェクトは、それが他のオブジェクトが配置されるのを防ぐ (prevents) 場合、メモリを 1 トン保持することができます。 オブジェクトが削除され (そして、これに依存していたものらが到達可能でなくなった) たら、解放できるメモリのサイズは、保持されたサイズ (retained size) と呼ばれます。
1.7.8 Q: constructor (コンストラクタ) と retained views (保持ビュー) に多くのデータがあります。 漏れがあるかどうか、発見するためにどこから調べ始めるべき?
あなたのツリーに保持された、従者が距離によってソートされれているように、最初のオブジェクトからの調査を始めることは一般によい考えである。 (まあ、 window までの距離です)。
最短距離で保持されたオブジェクトは、通常メモリーリークを引き起こすことに対するあなたの第一 (最初の) 候補です。
1.7.9 Q: 異なる Summary (概要)、Comparison (比較)、Dominators (ドミネータ) と Containment (封じ込め) ビューの違いは何ですか?
あなたは、画面の下部にある利用可能なさまざまなデータビューを切り替えることで、いくつかのマイレージ (走行距離) を得ることができます。
- Summary view [概要]ビューでは、コンストラクタの名前でグループ化された型に基づいて、オブジェクト(およびそのメモリ使用)を追い詰めることができます。 このビューには、DOMリークを追跡するために特に有用である。
- Comparison view 比較ビューでは、オブジェクトが正しくガベージコレクタによってクリーンアップされている表示することで、メモリリークを追跡するのに役立ちます。 一般的には、操作の前と後の 2つ (またはそれ以上) のメモリのスナップショットを記録し、比較するために使用。 このアイデアは、あなたに解放されたメモリと、参照 (リファレンス) カウント中でデルタを検査するには、メモリーリークの存在および原因を確認できるということです。
- Containment view 封じ込めビューは、私たちが何がそれらを周りに保っているかを調べるためにグローバルな名前空間 (すなわちウィンドウ) で参照されているオブジェクトを分析するのを支援し、 オブジェクト構造のより良いビューを提供します。 それはあなたがクロージャを分析し、低レベルでのオブジェクトに飛び込む (ダイブする) ことができます。
- Dominators view ドミネータビューは、オブジェクトへの予想外の参照がまだうろついていないか(つまり、これらは十分に含まれていること)と、 その削除/ガベージコレクションが実際に動作していることを確認するのに役立ちます。
1.7.10 Q: Heap (ヒープ) プロファイラのさまざまなコンストラクタ (グループ) エントリはどのように対応していますか?
- (global property) – ('window' のような)グローバルオブジェクトで、それが参照するオブジェクトとの間の中間のオブジェクト。 オブジェクトがコンストラクタ Person を使用して作成され、グローバルオブジェクトが保持されている場合は、保持パスが [global] > (global property) > Person のようになります。 これは標準 (norm ???) と対照をなす。そこでは、オブジェクトは直接参照する。 私たちは、パフォーマンス上の理由から中間 (intermediate) オブジェクトを持っている。 グローバルは定期的に変更され、非グローバルオブジェクトはグローバルには適用できないため、プロパティのアクセスの最適化は、よい仕事をする。
- (roots) – 保持する (retaining) ツリービューでのルートエントリは、選択されたオブジェクトに参照を持つ実体 (entities) です。 これらはさらに、それ自身の目的のためのエンジンで作成された参照でありえます。 エンジンは、オブジェクトを参照のキャッシュを持っていますが、しかし、そのようなすべての参照が弱く、真に強い参照がないことを考えると収集されてからオブジェクトを防ぐことはできません。
- (closure) – 関数クロージャーを通じてオブジェクトのグループへの参照のカウント
- (array, string, number, regexp) – 配列、文字列、数値、または正規表現を参照するプロパティを持つオブジェクト型のリスト
- (compiled code) – 単純に、コンパイルされたコードに関連するすべてのもの。 スクリプトは、関数に似ていますが、 <SCRIPT> 体 (body) に対応しています。 SharedFunctionInfos (SFI) は、機能やコンパイルされたコードの間に立っオブジェクトです。 SFI がない間、関数は、通常、コンテキストを持っています。
- HTMLDivElement, HTMLAnchorElement, DocumentFragment etc – 要素や、コードによって参照される特定の種類のドキュメントオブジェクトへの参照。
あなたが見ることがあるような、他のオブジェクトの多くは、おそらくあなたのコードのライフサイクル中に生成されたと下記のコントローラと同じように、 イベントリスナーだけでなく、カスタムオブジェクトを含めることができます。 :
1.7.11 Q: 図 (数字) に影響を及ぼしているかもしれないものとして、Chrome でオフする必要があることはありますか?
クロームDevToolsを使用したプロファイリングのいずれかのタイプを実行する場合、 それはあなたがすべての拡張機能を無効にしてシークレットモードでの実行または カスタム (custom) ユーザーデータディレクトリに Chrome を起動する、どちらかことをお勧めします (–user-data-dir="") 。
アプリケーション、拡張機能、さらにはコンソールログ情報は、あなたの図に数字上の暗黙の影響を及ぼす場合があります。また、できる限り信頼できるようにしておきたい。
1.7.12 Closing remarks 結びの言葉
今日の JavaScript エンジンは、多くの状況の私たちのコードによって生成されたごみを高度に自動的に清潔にすることができます。 それによると、彼らは唯一これまで行くことができる、そして私たちのアプリケーションは、まだ論理エラーによって引き起こされたメモリリークを起こしやすい傾向がある。 あなたのボトルネックを発見するために、かつ利用可能なツールを使用し、そして、覚えて、それを推測しない – それをテストします。
1.8 Supporting Demos サポートのデモ
1.8.1 Debugging Memory Leaks メモリリークをデバッグ
我々はこのガイドでそれらを言及したが、種々のメモリの問題をテストするためのエンド·ツー·エンドの例の良いセット、 メモリリークするDOMノードの成長に至るまでは、以下に要約されています。 あなた自身の、より複雑なページやアプリケーション上でツールを使用する前に、それらを試したいことがあります。
- Example 1: Growing memory 成長するメモリ
- Example 2: Garbage collection in action アクションでのガベージコレクション
- Example 3: Scattered objects 散乱オブジェクト
- Example 4: Detached nodes 独立したノード
- Example 5: Memory and hidden classes メモリと隠しクラス
- Example 6: Leaking DOM nodes 漏れ DOM ノード
- Example 7: Eval is evil (almost always) eval は(ほとんどの場合)悪である
- Example 8: Recording heap allocations 記録ヒープ割り当て
- Example 9: DOM leaks bigger than expected DOM は、予想よりも大きなリークする
- Example 10: Retaining path 保持パス
- Example 11: Last exercise 最後の練習
追加のデモは次のものが利用可能です : (Last updated 2013-11-04)
- Gathering Scattered Objects 散在するオブジェクトの収集
- Verifying Action Cleanness 検証アクション清浄度
- Exploring the Heap Contents ヒープの内容を探る
- Uncovering DOM Leaks DOM リークを暴く
- Finding Accumulation Points 累積ポイントを見つける
1.9 Community Resources コミュニティリソース
ウェブ・アプリケーションに Chrome DevTools を使用して、見つけること および固定メモリ問題についてのコミュニティーによって書かれた多くの優れた資源がある。 下記は有用であると感じるかもしれないいくらかの選択である :
- memory by Gonzalo Ruiz de Villa Chrome DevTools でメモリリークを発見し、デバッグ
- JavaScript Profiling With The Chrome Developer Tools | Smashing Coding DevTools で JavaScript のプロファイリング
- Effectively managing memory at Gmail scale - HTML5 Rocks GMail のスケールでの効果的なメモリ管理
- Chrome DevTools Revolutions 2013 - HTML5 Rocks Chrome DevTools 革命 2013
- Google Chrome DevTools: Rendering & Memory profiling on Open Academ… DevTools レンダリングおよびメモリプロファイリング
- Improving Web App Performance With the Chrome DevTools Timeline and Profiles DevTools タイムラインとプロファイルとのパフォーマンスの最適化
Footnotes:
DEFINITION NOT FOUND.