メモリ ツールを使用してヒープ スナップショットを記録する

メモリ ツールのヒープ プロファイラーを使用して、次の操作を行います。

  • JavaScript ヒープ (JS ヒープ) スナップショットを記録します。
  • メモリ グラフを分析します。
  • スナップショットを比較します。
  • メモリ リークを見つけます。

DevTools ヒープ プロファイラーには、ページの JavaScript オブジェクトと関連する DOM ノードで使用されるメモリ分散が表示されます。 「メモリ用語」の「オブジェクト保持ツリー)」も参照してください。

スナップショットを取得する

  1. DevTools で、 メモリ ツールを開きます。

  2. [プロファイルの種類の選択] セクションで、[ヒープ スナップショット] オプション ボタンを選択します。

  3. [スナップショットの取得] ボタンをクリックし、[開始] をクリックします。 または、 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 は含まれません。
HTMLDivElementHTMLAnchorElementDocumentFragment など。 コードによって参照される特定の型の要素またはドキュメント オブジェクトへの参照。
(オブジェクト図形) V8 (Microsoft Edge の JavaScript エンジン) がオブジェクトのプロパティの理解とインデックス付けに使用する非表示のクラスと記述子配列への参照。 「HiddenClasses と DescriptorArrays」を参照してください。
(BigInt) BigInt オブジェクトへの参照。これは、Number オブジェクトで表すには大きすぎる値を表し、操作するために使用されます。 「BigInt」を参照してください。

比較ビュー

複数のスナップショットを相互に比較して、リークされたオブジェクトを検索します。 通常、ドキュメントを開いて閉じるなどの直接操作と逆操作のペアは、ガベージのままにしないでください。

特定のアプリケーション操作でリークが発生しないことを確認するには:

  1. 操作を実行する前に、ヒープスナップショットを取ります。

  2. 操作を実行します。 つまり、リークを引き起こしている可能性のあるページと何らかの方法で対話します。

  3. 逆操作を実行します。 つまり、反対の操作を行い、数回繰り返します。

  4. 2 つ目のヒープ スナップショットを取り、スナップショット 1 と比較して、このヒープのビューを [比較] に変更します。

[比較] ビューには、2 つのスナップショットの違いが表示されます。 エントリの合計を展開すると、追加および削除されたオブジェクト インスタンスが表示されます。

比較ビュー

Containment ビュー

Containment ビューは、基本的にアプリケーションのオブジェクト構造の "鳥瞰図" です。 これにより、関数のクロージャ内をピークしたり、JavaScript オブジェクトを構成する仮想マシン (VM) 内部オブジェクトを観察したり、アプリケーションが非常に低いレベルで使用するメモリ量を把握したりできます。

Containment ビューのエントリ ポイント 説明
DOMWindow オブジェクト JavaScript コードのグローバル オブジェクト。
GC ルート VM のガベージによって使用される実際の GC ルート。 GC ルートは、組み込みのオブジェクト マップ、シンボル テーブル、VM スレッド スタック、コンパイル キャッシュ、ハンドル スコープ、およびグローバル ハンドルで構成されます。
ネイティブ オブジェクト JavaScript 仮想マシン (JavaScript VM) 内のブラウザー オブジェクトを "プッシュ" して、オートメーション (DOM ノード、CSS ルールなど) を許可します。

Containment ビュー

スナップショットのクロージャを区別するための名前付け関数

関数に名前を付け、スナップショットのクロージャを簡単に区別できるようにします。 たとえば、次の例では、名前付き関数は使用しません。

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 ツリーになります。

DOM サブツリー

デモ 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 にすべての文字列を保存する

メモリ ツールは、ヒープ スナップショットからすべての文字列オブジェクトを含む JSON ファイルをエクスポートします。

JSON ファイル内のヒープ スナップショットからの文字列

注:

このページの一部は、 Google によって 作成および共有され、 クリエイティブ・コモンズ属性 4.0 国際ライセンスに記載されている条件に従って使用される作業に基づく変更です。 元のページはこちらで、Meggin Kearney (Technical Writer) によって作成されています。

クリエイティブ・コモンズ・ライセンス この作品は 、クリエイティブ・コモンズ属性4.0国際ライセンスに基づきライセンスされています