メモリ ツールを使用してヒープ スナップショットを記録する
メモリ ツールのヒープ プロファイラーを使用して、次の操作を行います。
- JavaScript ヒープ (JS ヒープ) スナップショットを記録します。
- メモリ グラフを分析します。
- スナップショットを比較します。
- メモリ リークを見つけます。
DevTools ヒープ プロファイラーには、ページの JavaScript オブジェクトと関連する DOM ノードで使用されるメモリ分散が表示されます。 「メモリ用語」の「オブジェクト保持ツリー)」も参照してください。
スナップショットを取得する
DevTools で、 メモリ ツールを開きます。
[プロファイルの種類の選択] セクションで、[ヒープ スナップショット] オプション ボタンを選択します。
[スナップショットの取得] ボタンをクリックし、[開始] をクリックします。 または、 Ctrl + E (Windows、Linux) または Command + E (macOS) を押します。
スナップショット は、最初はレンダラー プロセス メモリに格納されます。 スナップショットは、スナップショット アイコンをクリックして表示すると、必要に応じて DevTools に転送されます。
スナップショットが DevTools に読み込まれ、解析されると、スナップショット タイトルの下の番号が表示され、到達可能な JavaScript オブジェクトの合計サイズが表示されます。
注:
スナップショットには到達可能なオブジェクトのみが含まれます。 また、スナップショットを取得すると、常にガベージ コレクションから始まります。
スナップショットをクリアする
[ すべてのプロファイルのクリア ] アイコンをクリックして、スナップショット (DevTools とレンダラー プロセスに関連付けられているメモリの両方) を削除します。
DevTools ウィンドウを閉じても、レンダラー プロセスに関連付けられているメモリからプロファイルは削除されません。 DevTools を再度開くと、以前に作成したすべてのスナップショットがスナップショットの一覧に再表示されます。
デモ Web ページ: 例 3: 分散オブジェクト
- このデモの例の Web ページを開きます。例 3: 新しいウィンドウまたはタブに 散在するオブジェクト 。ヒープ プロファイラーを使用してプロファイルします。 多数の (オブジェクト) 項目の割り当てが表示されます。
スナップショットの表示
さまざまなタスクについて、さまざまな観点からスナップショットを表示します。
サマリー ビュー には、コンストラクター名でグループ化されたオブジェクトが表示されます。 これを使用して、コンストラクター名でグループ化された型に基づいてオブジェクト (およびメモリ使用量) を検索します。 概要ビュー は、 DOM リークを追跡するために特に役立ちます。
比較ビュー。 2 つのスナップショットの違いを表示します。 操作の前後から 2 つ (またはそれ以上) のメモリ スナップショットを比較する場合に使用します。 解放されたメモリと参照カウントでデルタを検査すると、メモリ リークの存在と原因を確認できます。
Containment ビュー。 ヒープの内容の探索を許可します。 Containment ビュー を使用すると、オブジェクト構造をより適切に表示できます。グローバル名前空間 (ウィンドウ) で参照されているオブジェクトを分析して、オブジェクトを保持している内容を調べるのに役立ちます。 クロージャを分析し、低レベルでオブジェクトを掘り下げるために使用します。
ビューを切り替えるには、ビューの上部にあるセレクターを使用します。
注:
すべてのプロパティが JavaScript ヒープに格納されているわけではありません。 ネイティブ コードを実行するゲッターを使用して実装されたプロパティはキャプチャされません。 また、数値などの文字列以外の値はキャプチャされません。
概要ビュー
最初に、[概要] ビューにスナップショットが開き、オブジェクトの合計が表示されます。これは、インスタンスを表示するために展開できます。
最上位のエントリは"合計" 行です。
最上位のエントリ | 説明 |
---|---|
コンストラクター | このコンストラクターを使用して作成されたすべてのオブジェクトを表します。 |
Distance | ノードの最短の単純パスを使用して、ルートまでの距離を表示します。 |
浅いサイズ | 特定のコンストラクター関数によって作成されたすべてのオブジェクトの浅いサイズの合計を表示します。 浅いサイズは、オブジェクトによって保持されるメモリのサイズです (通常、配列と文字列の浅いサイズが大きくなります)。 「オブジェクト サイズ」を参照してください。 |
保持サイズ | 同じオブジェクト のセット間で保持される最大サイズを表示します。 オブジェクトが削除された後に解放できるメモリのサイズ (および依存オブジェクトに到達できなくなります) は、保持サイズと呼ばれます。 「オブジェクト サイズ」を参照してください。 |
上のビューで合計行を展開すると、すべてのインスタンスが表示されます。 インスタンスごとに、浅いサイズと保持されたサイズが対応する列に表示されます。 文字の後の @
数値はオブジェクトの一意の ID であり、オブジェクトごとにヒープ スナップショットを比較できます。
- 黄色のオブジェクトには JavaScript 参照があります。
- 赤いオブジェクトはデタッチされたノードです。 デタッチされたノードは、背景が黄色のノードから参照されます。
ヒープ プロファイラーのコンストラクター (グループ) エントリ
ヒープ プロファイラー内のさまざまなコンストラクター (グループ) エントリは、次の種類のオブジェクトに対応しています。
コンストラクター (グループ) エントリ | 説明 |
---|---|
(global プロパティ) | グローバル オブジェクト (など window ) と、そのオブジェクトによって参照されるオブジェクトの間の中間オブジェクト。 オブジェクトがコンストラクター Person を使用して作成され、グローバル オブジェクトによって保持されている場合、保持パスは として [global] > (global property) > Person 表されます。 これは、オブジェクトが直接相互に参照する標準とは対照的です。 中間オブジェクトは、パフォーマンスのために存在します。 グローバルは定期的に変更され、プロパティ アクセスの最適化はグローバル以外のオブジェクトに対して適切なジョブを実行します。グローバルオブジェクトには適用できません。 |
(ルート) | 保持ツリー ビューのルート エントリは、選択したオブジェクトへの参照を持つエンティティです。 これらは、独自の目的でエンジンによって作成された参照でもあります。 エンジンにはオブジェクトを参照するキャッシュがありますが、そのような参照はすべて脆弱であり、本当に強力な参照がないため、オブジェクトの収集を妨げるわけではありません。 |
(クロージャ) | 関数のクロージャを使用したオブジェクトのグループへの参照の数。 |
(配列、文字列、数値、regexp) | Array、String、Number、または正規表現を参照するプロパティを持つオブジェクト型のリスト。 |
(コンパイル済みコード) | コンパイル済みコードに関連するすべてのもの。 スクリプトは関数に似ていますが、本文に <script> 対応します。 SharedFunctionInfos (SFI) は、関数とコンパイルされたコードの間に存在するオブジェクトです。 関数には通常コンテキストが含まれますが、SF は含まれません。 |
HTMLDivElement、 HTMLAnchorElement、 DocumentFragment など。 | コードによって参照される特定の型の要素またはドキュメント オブジェクトへの参照。 |
(オブジェクト図形) | V8 (Microsoft Edge の JavaScript エンジン) がオブジェクトのプロパティの理解とインデックス付けに使用する非表示のクラスと記述子配列への参照。 「HiddenClasses と DescriptorArrays」を参照してください。 |
(BigInt) | BigInt オブジェクトへの参照。これは、Number オブジェクトで表すには大きすぎる値を表し、操作するために使用されます。 「BigInt」を参照してください。 |
比較ビュー
複数のスナップショットを相互に比較して、リークされたオブジェクトを検索します。 通常、ドキュメントを開いて閉じるなどの直接操作と逆操作のペアは、ガベージのままにしないでください。
特定のアプリケーション操作でリークが発生しないことを確認するには:
操作を実行する前に、ヒープスナップショットを取ります。
操作を実行します。 つまり、リークを引き起こしている可能性のあるページと何らかの方法で対話します。
逆操作を実行します。 つまり、反対の操作を行い、数回繰り返します。
2 つ目のヒープ スナップショットを取り、スナップショット 1 と比較して、このヒープのビューを [比較] に変更します。
[比較] ビューには、2 つのスナップショットの違いが表示されます。 エントリの合計を展開すると、追加および削除されたオブジェクト インスタンスが表示されます。
Containment ビュー
Containment ビューは、基本的にアプリケーションのオブジェクト構造の "鳥瞰図" です。 これにより、関数のクロージャ内をピークしたり、JavaScript オブジェクトを構成する仮想マシン (VM) 内部オブジェクトを観察したり、アプリケーションが非常に低いレベルで使用するメモリ量を把握したりできます。
Containment ビューのエントリ ポイント | 説明 |
---|---|
DOMWindow オブジェクト | JavaScript コードのグローバル オブジェクト。 |
GC ルート | VM のガベージによって使用される実際の GC ルート。 GC ルートは、組み込みのオブジェクト マップ、シンボル テーブル、VM スレッド スタック、コンパイル キャッシュ、ハンドル スコープ、およびグローバル ハンドルで構成されます。 |
ネイティブ オブジェクト | JavaScript 仮想マシン (JavaScript VM) 内のブラウザー オブジェクトを "プッシュ" して、オートメーション (DOM ノード、CSS ルールなど) を許可します。 |
スナップショットのクロージャを区別するための名前付け関数
関数に名前を付け、スナップショットのクロージャを簡単に区別できるようにします。 たとえば、次の例では、名前付き関数は使用しません。
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;
}
デモ Web ページ: 例 7: Eval は悪です
クロージャがメモリに与える影響を分析するには、次の例を試してください。デモ Web ページ の例 7: Eval は 新しいウィンドウまたはタブで悪です。
デモ Web ページ: 例 8: ヒープ割り当ての記録
また、ヒープ割り当ての記録を行うこの例で、上記のデモをフォローアップすることもできます。デモ Web ページの例 8: 新しいウィンドウまたはタブでの ヒープ割り当ての記録 を開きます。
ノードの種類でヒープ スナップショットをフィルター処理する
フィルターを使用して、ヒープ スナップショットの特定の部分に焦点を当てます。 たとえば、ヒープの文字列または配列にのみ関心がある場合は、ノードの種類でフィルター処理できます。
メモリ ツールでヒープ スナップショット内のすべてのオブジェクトを見ると、特定のオブジェクトに焦点を当てたり、パスを保持することが困難になる場合があります。 ヒープのスナップショットを確認する場合は、[ノードの種類] フィルターを使用して、特定の種類のノードのみに焦点を当てます。 たとえば、ヒープ内の配列と文字列オブジェクトのみを表示するには、[ノードの種類] フィルターで [配列] エントリと [文字列] エントリを選択します。
カラー コーディングを検索する
オブジェクトのプロパティとプロパティ値は、さまざまな型を持ち、それに応じて色付けされます。 各プロパティには、次の 4 種類のいずれかが含まれます。
プロパティの種類 | 説明 |
---|---|
a: プロパティ | 名前を持つ通常のプロパティ。(ドット) 演算子を使用して. アクセスするか、(角かっこ) 表記 (例: ["foo bar"] ) を使用して[ ] アクセスします。 |
0: 要素 | 数値インデックスを持つ通常のプロパティ。(角かっこ) 表記を使用して [ ] アクセスします。 |
a: context var | 関数のクロージャ内から変数名でアクセスできる、関数コンテキスト内の変数。 |
a: システム プロップ | JavaScript VM によって追加されたプロパティ。JavaScript コードからはアクセスできません。 |
として System
指定されたオブジェクトには、対応する JavaScript 型がありません。 それぞれは、Javascript VM のオブジェクト システム実装の一部です。 V8 は、ユーザーの JS オブジェクトと同じヒープ内のほとんどの内部オブジェクトを割り当てます。 そのため、これらは単なる V8 内部です。
特定のオブジェクトを検索する
収集されたヒープ内のオブジェクトを検索するには、 Ctrl + F キー を使用して検索し、オブジェクト ID を指定します。
DOM リークを検出する
ヒープ プロファイラーでは、ブラウザーネイティブ オブジェクト (DOM ノード、CSS ルール) と JavaScript オブジェクト間の双方向の依存関係を反映できます。 これは、切断された DOM サブツリーが浮かび上がり忘れたために発生する、見えないリークを検出するのに役立ちます。
DOM リークは、思ったより大きくなる可能性があります。 次の例を考えてみましょう。 ガベージ コレクションはいつ #tree
ですか?
var treeRef = document.querySelector("#tree");
var leafRef = document.querySelector("#leaf");
var body = document.querySelector("body");
body.removeChild(treeRef);
//#tree in not GC yet due to treeRef
treeRef = null;
//#tree is not GC yet due to indirect reference from leafRef
leafRef = null;
//#NOW can be #tree GC
は #leaf
、関連する親 (parentNode) への参照を保持し、 まで #tree
再帰的に保持するため、null 化された場合 leafRef
にのみ、ガベージ コレクション (GC) の候補の下にある #tree
WHOLE ツリーになります。
デモ Web ページ: 例 6: DOM ノードのリーク
例の Web ページ例 6: 新しいウィンドウまたはタブで DOM ノードをリーク し、DOM ノードがリークする可能性がある場所と、そのようなリークを検出する方法を理解します。
デモ Web ページ: 例 9: DOM リークが予想よりも大きい
例の Web ページ 例 9: DOM リークが、 新しいウィンドウまたはタブで予想以上に大きくなっています。
DOM リークとメモリ分析の基礎の詳細については、「Gonzalo Ruiz de Villa による Microsoft Edge DevTools を使用したメモリ リークの検索とデバッグ」をチェックします。
ヒープ スナップショットから JSON への文字列の保存とエクスポート
メモリ ツールでヒープ スナップショットを取得する場合は、スナップショットから JSON ファイルにすべての文字列オブジェクトをエクスポートできます。 メモリ ツールの [コンストラクター] セクションで、エントリの横にある [すべてファイルに保存] ボタンを(string)
クリックします。
メモリ ツールは、ヒープ スナップショットからすべての文字列オブジェクトを含む JSON ファイルをエクスポートします。
注:
このページの一部は、 Google によって 作成および共有され、 クリエイティブ・コモンズ属性 4.0 国際ライセンスに記載されている条件に従って使用される作業に基づく変更です。 元のページはこちらで、Meggin Kearney (Technical Writer) によって作成されています。