ホームページ  >  記事  >  ウェブフロントエンド  >  Chrome デベロッパー ツールの JavaScript メモリ分析のグラフィカルな紹介

Chrome デベロッパー ツールの JavaScript メモリ分析のグラフィカルな紹介

黄舟
黄舟オリジナル
2017-03-14 15:28:102152ブラウズ

メモリ リーク は、コンピュータの利用可能なメモリが徐々に減少することです。これは、プログラムが使用する一時メモリの解放に継続的に失敗する場合に発生します。 JavaScript Web アプリケーションでは、リーク やオーバーフローなど、ネイティブ アプリケーションで発生するメモリ関連の問題が頻繁に発生します。Web アプリケーションは、 ガベージ コレクションの一時停止 にも対処する必要があります。

JavaScript は自動メモリ管理のためにガベージ コレクションを使用しますが、効果的なメモリ管理は依然として重要です。この記事では、JavaScript Web アプリケーションのメモリ問題の分析について説明します。機能について学習するときは、必ずサンプルを試して、これらのツールが実際にどのように機能するかについての理解を深めてください。この記事で使用されている用語を理解するには、「Memory 101」ページを読んでください。 注: これから使用する機能の一部は、現在ブラウザの Chrome Canary バージョンでのみ利用可能です。アプリケーションのメモリ問題を分析するための最適なツールを入手するには、このバージョンを使用することをお勧めします。

考慮する必要がある質問

一般に、メモリ リークの問題が発生したと思われる場合は、次の 3 つの質問について考える必要があります:

  • 私のページはメモリを使いすぎていませんか? - 時間 タイムライン メモリ ビューと Chrome タスク マネージャーは、メモリの使用量が多すぎるかどうかを確認するのに役立ちます。メモリ ビューは、ページ レンダリング中に DOM ノード 数、document のドキュメント数、および JS イベントリッスン数を追跡できます。経験則として: 不要になった DOM 要素への 参照 を避け、不要なイベント リスナーを削除し、使用しない可能性のある大きなデータの塊を保存するときは注意してください。

  • 私のページにメモリリークはありますか? - ObjectAllocation Tracker (Object allocation Tracker) は、JS オブジェクトの割り当てをリアルタイムで表示することで、リークを特定するのに役立ちます。ヒープ アナライザー (Heap Profiler) を使用して JS ヒープ スナップショットを生成し、メモリ マップを分析してスナップショット間の差異を比較することで、ガベージ コレクションによってクリーンアップされていないオブジェクトを見つけることもできます。

  • ページのガベージ コレクションはどのくらいの頻度で強制されますか? - ページのガベージ コレクションが非常に頻繁に行われる場合は、ページにメモリが頻繁に割り当てられる可能性があることを意味します。タイムライン メモリ ビューは、関心のある一時停止を見つけるのに役立ちます。

用語と基本概念

このセクションでは、メモリ分析で使用される一般的な用語を紹介します。これらの用語は、他の言語のメモリ分析を行うツールにも適用されます。ここでの用語と概念は、ヒープ プロファイラー UI ツールと関連ドキュメントで使用されます。

これらは、メモリ分析ツールを効果的に使用する方法に慣れるのに役立ちます。 Java、.NET などの言語のメモリ分析ツールを使用したことがある場合、これはレビューになります。

オブジェクトサイズ

メモリを、基本的な型 (数値や文字列など) とオブジェクト (連想配列) のグラフとして考えてください。一連の関連点をまとめた次の図のようになります。

オブジェクトにはメモリを使用する 2 つの方法があります:

  • オブジェクト自体が

  • を直接使用し、他のオブジェクトへの参照を暗黙的に維持します。この方法は、それらのオブジェクトのガベージ コレクション (GC と呼ばれます) を防ぎます。オブジェクトの自動リサイクル。

DevTools のヒープ プロファイラー (DevTools の [プロファイル] タブにあるメモリ問題の分析に使用されるツール、ヒープ プロファイラー) を使用すると、さまざまな情報が表示される列がいくつかあることに驚くかもしれません。そのうちの 2 つは、直接占有メモリ (浅いサイズ)占有総メモリ (保持サイズ) です。では、これらは何を意味するのでしょうか?

メモリを直接占有(浅いサイズ、参照されるオブジェクトが占有するメモリを除く)

これはオブジェクト自体が占有するメモリです。

典型的な JavaScript オブジェクト は、オブジェクトを記述し、その直接値を保存するために予約されたメモリを持っています。一般に、直接メモリを大幅に占有するのは配列と文字列だけです (浅いサイズ)。ただし、文字列と配列は多くの場合、メイン データ部分をレンダラー メモリに保存し、JavaScript オブジェクト スタック内の小さなラッパー オブジェクトのみを公開します。

レンダラー メモリとは、分析するページのレンダリング プロセスで使用されるすべてのメモリを指します。ページ自体のメモリ + ページ内の JS ヒープによって使用されるメモリ + 関連するワーカー プロセス (ワーカー) で使用される JS ヒープメモリへのページによってトリガーされます。ただし、小さなオブジェクトは、他のオブジェクトがガベージによって自動的に収集されないようにすることで、間接的に大量のメモリを占有する可能性があります。

占有される合計メモリ (参照オブジェクトによって占有されるメモリを含む保持サイズ)

オブジェクトが 削除されると、そのオブジェクトが参照する依存オブジェクトは GC ルート (GC ルート) から参照できなくなり、占有されます。メモリは解放され、オブジェクトによって占有されるメモリの合計には、これらの依存オブジェクトによって占有されるメモリが含まれます。

GC rootcontrollers(handles)で構成されており、これらのコントローラー(ローカルまたはグローバル)は、ビルトイン関数(ネイティブコード)によってV8に組み込まれています。エンジンの外にオブジェクトが作成されます。これらのコントローラーはすべて、ヒープ スナップショットの GC ルート > ハンドル スコープ および GC ルート >グローバル ハンドラー にあります。この記事でこれらのコントローラーを紹介する場合、ブラウザーの実装を深く理解していないと混乱する可能性があります。 GC ルートとコントローラーについてはあまり心配する必要はありません。

ユーザーにとって重要ではない内部 GC ルートが多数あります。アプリケーションの観点から見ると、次のような状況があります:

注:

ヒープスナップショットの作成時に、コンソールでコードを実行したり、デバッグブレークポイントを有効にしたりしないことをお勧めします。 メモリ マップはルートから始まります。ルートはブラウザの window オブジェクトまたは Node.js

モジュールの Global オブジェクト。これらのオブジェクトがメモリからどのように再利用されるかは、ユーザーの制御下にはありません。

window对象或Node.js模块Global

GC ルートによってトラバースできないオブジェクトはメモリがリサイクルされます。

注:

直接占有メモリフィールドと総メモリ占有フィールドのデータはバイト単位で表されます。 オブジェクトが占有するメモリツリーの合計

ヒープは、相互に関連するさまざまなオブジェクトで構成されるネットワーク構造であることを以前に学びました。デジタルの世界では、この構造は

グラフ

またはメモリ グラフと呼ばれます。グラフはエッジで接続されたノードで構成されており、それらはすべてラベル付けされています。

  • ノード

    (またはオブジェクト) ノードのラベル名は、ノードを作成したコンストラクター関数の名前によって決定されます

  • エッジ

    ラベル名は属性Nameです

  • このドキュメントの後半では、ヒープ アナライザーを使用してスナップショットを生成する方法を学習します。以下の図のヒープ アナライザーによって生成されたスナップショットから、距離フィールドがわかります。これは、オブジェクトから GC ルートまでの距離を指します。同じタイプのすべてのオブジェクトが同じ距離にあるが、小さなサブセットがさらに離れている場合は、調査が必要な問題がある可能性があります。

ドミネーター

各オブジェクトにはドミネーターがあるため、ドミネーターはツリー構造のようなものです。オブジェクトのコントローラは、それが支配するオブジェクトを直接参照することはできません。つまり、支配されるオブジェクトのツリー構造は、グラフ内のスパニング ツリーではありません。

上の図:

  • ノード 1 がノード 2 を支配する

  • ノード 2 がノード 3、4、6 を支配する

  • ノード 3 がノード 5 を支配する

  • ノード 5 がノード 8 を支配

  • ノード 6 がノード 7 を支配します

以下の例では、ノード #3#10的支配者,但#7也在每个从GC到#10 がパスに表示されます。このように、B オブジェクトがルート ノードから A オブジェクトまでのすべてのパスに出現する場合、B オブジェクトは A オブジェクトの支配的なオブジェクトになります。

V8 の概要

このセクションでは、V8 JavaScript 仮想マシン (V8 VM または VM) に関連するいくつかのメモリ関連の概念について説明します。メモリを分析する場合、ヒープ スナップショットを理解するためにこれらの概念を理解しておくと役立ちます。

JavaScript オブジェクトの説明

には 3 つのプリミティブ型があります:

  • 数値 (3.14159.. など)

  • ブール値 (true または false)

  • 文字型 (String s) ('Wer など)ナーハイゼンベルク')

それらは他の値を参照せず、葉ノードまたは終端ノードのみになります。

数値は、次の 2 つの方法のいずれかで保存されます:

  • 31 ビットの 整数直接値: 小さい整数 (小さい 整数)(SMI)、または

  • ヒープオブジェクト。ヒープ値として参照されます。ヒープ値は、double などの SMI ストレージに適さないデータを保存するために、またはこの値の属性値を設定するなど、値をボックス化する必要がある場合に使用されます。

    キャラクター
  • データは、

VMヒープ
    、または
  • 外部

    レンダラーメモリ
  • の2つの方法で保存されます。このとき、VM ヒープに直接コピーされるのではなく、Web ページ パッケージに保存されているスクリプト リソースやその他のコンテンツなどの保存場所にアクセスするためのラッパー オブジェクトが作成されます。
  • 新しく作成された JavaScript オブジェクトには、JavaScript ヒープ (または

    VM ヒープ
  • ) 上のメモリが割り当てられます。これらのオブジェクトは V8 のガベージ コレクターによって管理され、それらへの強い参照がある限りメモリ内に残ります。

ローカル オブジェクト は、ヒープ オブジェクトとは異なり、

ライフサイクル

中に V8 ガベージ コレクターによって処理されず、JavaScript ラッピング オブジェクトによってのみ参照できます。 連結文字列は、一対の文字列を結合することによって形成されるオブジェクトであり、結合の結果です。

接続文字列

は、必要な場合にのみマージされます。連結された文字列と同様に、部分文字列を構築する必要があります。 例: a

b

を連結すると、連結の結果を表すために使用される文字列 (a, b) が得られます。後でこの結果を d と連結すると、別の連結された文字列 ((a, b), d) が得られます。 配列 (Array

s)

- 配列は数字キーを持つオブジェクトです。これらは、大量のデータを保存する場合に V8 エンジンで広く使用されています。辞書などのキーと値のペアを持つオブジェクトは、配列を使用して実装されます。 典型的な JavaScript オブジェクトは、2 つの配列タイプのいずれかに格納できます:

名前付きプロパティ、

  • 数値要素

  • プロパティが少数しかない場合、それらは JavaScript オブジェクトに直接格納されます自体。

Map

- オブジェクトタイプとその構造を記述するために使用されるオブジェクト。たとえば、マップを使用してオブジェクトの構造を記述し、オブジェクトのプロパティにすばやくアクセスできるようにしますオブジェクト グループ 各ローカル オブジェクト グループは、相互に関連するオブジェクトのセットで構成されます。たとえば、DOM サブツリーでは、各ノードは、関連付けグラフを形成する親要素、次の子要素、および次の兄弟要素にアクセスできます。ネイティブ要素は JavaScript ヒープ内で表されないことに注意してください。そのため、ラッピング オブジェクトの作成中にネイティブ要素のサイズは 0 になります。

各ラッパー オブジェクトにはローカル オブジェクトへの参照があり、これらのローカル オブジェクトに対する操作を渡すために使用されます。これらのローカル オブジェクトには、ラッピング オブジェクトへの参照もあります。ただし、これによって回復不能な ループ が作成されるわけではありません。GC は、ラップされたオブジェクトへの参照を持たなくなったローカル オブジェクトを識別して解放するのに十分な機能を備えています。ただし、ラッパー オブジェクトが解放されない場合は、すべてのオブジェクト グループと関連するラッパー オブジェクトが保持されます。

前提条件と役立つヒント

Chrome タスク マネージャー

注:メモリ分析に Chrome を使用する場合は、クリーンなテスト環境をセットアップするのが最善です

Chrome のメモリ マネージャーを開き、関連する操作を実行してメモリ フィールドを観察しますページ上で をクリックすると、この操作によってページが大量のメモリを占有するかどうかをすぐに判断できます。メモリ マネージャーは、Chrome メニュー > [ツール] から、または Shift + Esc キーを押すことで見つけることができます。

開いたら、ヘッダーを右クリックし、[JavaScript で使用されるメモリ] オプションを選択します。

DevTools タイムラインを通じてメモリの問題を特定します

問題を解決するための最初のステップは、問題が存在することを証明できるようにすることです。これには、問題のベースライン測定として機能する再現可能なテストを作成する必要があります。再現可能な手順がなければ、問題を確実に測定することはできません。言い換えれば、比較のためのベースラインがなければ、どのような変更が問題の原因となったのかを知る方法はありません。

タイムラインパネルは、プログラムに問題がある場合にそれを見つけるのに非常に役立ちます。 Web アプリケーションまたは Web サイトがロードされ、対話している瞬間が表示されます。すべてのイベント: リソースの読み込みから JavaScript のデコード、スタイル計算、ガベージ コレクションの一時停止、ページの再描画まで。すべてがタイムライン上に表示されます。

メモリの問題を分析する場合、タイムライン パネルのメモリ ビュー(メモリ ビュー)を使用して以下を観察できます:

  • 合計メモリ使用量 – メモリ使用量は増加しましたか?

  • DOM ノード数

  • ドキュメントの数

  • 登録されたイベントリスナーの数

メモリ分析中にメモリリークを特定する方法の詳細については、Zack Grossbart Chrome DevToolsを使用したメモリプロファイリングを参照してください

問題

まず最初に行うことは、メモリ リークの原因となっている可能性があるアクションを特定することです。これは、ページ上で発生するマウスオーバー、クリック、またはページ上のパフォーマンス低下を引き起こす可能性のあるその他のインタラクションなどのあらゆるイベントです。

タイムラインパネル (Ctrl+E または Cmd+E) で記録を開始し、テストするアクションを実行します。ガベージコレクションを強制するには、パネル上のゴミ箱アイコン (

) をクリックします。

ここでは、一部のポイントがガベージ コレクションされないメモリ リークの例を示します:

テストを繰り返した後、(メモリ パネルの上に) ギザギザのグラフィックが表示された場合は、有効期間の短いオブジェクトが多数存在します。プログラム内で。また、一連のアクションによってメモリが一定の範囲内に収まらず、DOM ノードの数が最初の数に戻らない場合は、メモリ リークが疑われる可能性があります。

メモリに問題があると判断したら、

プロファイルパネルヒーププロファイラーを使用して問題の原因を特定できます。

例: タイムラインを通じてメモリの問題を効果的に

練習分析するのに役立つ、メモリ増加の例を試してください。

Memory Recycler

Memory Collector (V8 のものなど) は、どのオブジェクトが live(live) であるかを特定できる必要がありますが、dead(garbage) とみなされるオブジェクトは 不可能です(unreachable)を参照。

JavaScript 実行時のロジック エラーにより、

ガベージ コレクション (GC) がガベージ オブジェクトの収集に失敗した場合、これらのガベージ オブジェクトはリサイクルできなくなります。このような状況では、最終的にアプリケーションの速度がどんどん遅くなります。

たとえば、コードを作成するときに、一部の

変数 とイベント リスナーは使用されなくなりますが、一部のコードでは引き続き参照されます。参照がまだ存在する限り、参照されたオブジェクトは GC によって正しくリサイクルできません。

アプリケーションの実行中に、一部の DOM オブジェクト 更新 削除された可能性があります。DOM オブジェクトを参照する変数を必ずチェックし、それらを null に設定してください。他のオブジェクト (または他の DOM 要素) を参照している可能性があるオブジェクトのプロパティを確認してください。成長する可能性のある変数キャッシュに注目してください。

ヒープアナライザー

スナップショットを取得します

プロファイルパネルで、ヒープスナップショットを取得を選択し、開始をクリックするか、Cmd + EまたはCtrl + Eを押します:

スナップショットは最初にレンダリングに保存されます。サーバープロセスのメモリ内。これらはオンデマンドで DevTools にインポートされ、スナップショット ボタン をクリックすると表示されます。スナップショットが DevTools にロードされて表示されると、スナップショット タイトルの下の数字は、到達可能な JavaScript オブジェクトによって占有されるメモリの合計量を示します。

例: action でガベージ コレクションの例を試し、タイムライン パネルでメモリ使用量を監視します。

スナップショットをクリア

すべてのスナップショットをクリアするには、すべてクリアボタンアイコン()をクリックします:

注: DevToolsウィンドウを閉じると、収集されたスナップショットをレンダリングメモリ。 DevTools を再度開くと、以前のスナップショット リストがまだ残っています。

前に述べたことを思い出してください。スナップショットを生成するときに、DevTools で GC を強制できます。スナップショットを取得すると、GC が自動的に実行されます。タイムラインのゴミ箱(ガベージコレクション)ボタン(

)をクリックすると簡単にガベージコレクションを行うことができます。

例: 分散オブジェクトを試し、ヒープ プロファイラーで分析します。アイテム(オブジェクト)のコレクションをご覧いただけます。

スナップショットビューの切り替え

スナップショットでは、さまざまなタスクに応じてビューを切り替えることができます。図のように選択ボックスを切り替えることができます。

以下は 3 つのデフォルトのビューです:

  • Summary (要約) - コンストラクター 名の分類ごとにオブジェクトを表示します。

  • 比較 (コントラスト) )

    - 2 つのスナップショット間のオブジェクト間の差異を表示します。

  • Containment (コントロール)

    - ヒープの内容を検出するために使用できます。

ビューは

Set で開くことができます。 tings パネル – ドミネーター ツリーを表示し、メモリの成長ポイントを見つけるために使用できます。 さまざまな色でオブジェクトを区別する

オブジェクトの属性と属性値にはさまざまな種類があり、色によって自動的に区別されます。各プロパティは、次の 4 つのいずれかです:

    a:property
  • - 名前

    でインデックス付けされた通常のプロパティ。 . (ドット) 演算子 、または [] (角かっこ) によって参照されます。 "foo bar"];

    0:element
  • - [] (角括弧) で参照される通常の属性。関数内の属性。 context、名前で参照されます。
  • a:system prop
  • - JavaScript VM によって追加されるプロパティであり、JavaScript コードからはアクセスできません。
  • という名前のオブジェクトには、対応する JavaScript タイプがありません。これらは JavaScript VM オブジェクト システムに組み込まれています。 V8 では、ほとんどの組み込みオブジェクトとユーザー JS オブジェクトが同じヒープに配置されます。しかし、それらは単なる V8 の
  • 内部オブジェクト
  • です。

    詳細を表示

    概要ビュー (概要ビュー)
スナップショットを開きます。デフォルトで概要ビューに表示され、オブジェクトの総数が表示されます。また、展開して特定のコンテンツを表示することもできます。最初は、スナップショットが開きます。概要ビュー: オブジェクトの合計を

表示Systemし、展開してインスタンスを表示できます:

最初のレベルは「全体」行で、以下が表示されます:

  • Constructor (コンストラクター) このコンストラクターによって生成されたすべてのオブジェクトを表します

  • オブジェクト 内のオブジェクト のインスタンスの数Count 列は、対応するコンストラクターによって生成されたオブジェクトの浅いサイズ (直接占有されたメモリ) の合計数を示します。 Retained size 列は、対応するオブジェクトによって占有された最大メモリを示します。オブジェクトから GC ルートまでの最短距離

  • 行全体を展開すると、すべてのオブジェクト インスタンスが表示されます。占有される直接メモリと、各インスタンスによって占有される合計メモリがそれに応じて表示されます。 @ 記号の後の数字はオブジェクトの一意の ID であり、これを使用して、オブジェクトごとに異なるスナップショットを比較できます。 例: アウトライン ビューの使用方法を学ぶには、この例 (新しいタブで開きます) を試してください。

  • 黄色のオブジェクトは JavaScript によって参照され、赤色のオブジェクトは黄色の背景色の参照によって区切られたノードであることに注意してください。
  • 比較ビュー このビューは、異なるスナップショットを比較してスナップショット間の違いを見つけたり、メモリ リークのあるオブジェクトを見つけたりするために使用されます。アプリケーションの特定の操作にリークがないことを証明するには (たとえば、一般に、ドキュメントを開いて閉じるなど、操作と元に戻すアクションのペアではリークが発生しません)、次の手順を試すことができます。

  • 操作の前にヒープ スナップショットを作成します。

  • 操作を実行します (リークの原因となると考えられる操作を実行します)。

前の操作を元に戻します (前の操作の逆の操作を数回繰り返します)。 ;

2 番目のスナップショットを取得し、ビューを比較ビューに切り替えて、スナップショット 1 と比較します。

    比較ビューでは、2 つのスナップショットの違いが表示されます。一般カテゴリを展開すると、追加および削除されたオブジェクトが表示されます:
  1. 例: 比較ビューを使用してメモリ リークを特定する方法を学習するには、例 (新しいタブで開きます) を試してください。

    包含ビュー
  2. コントロール ビューは、アプリケーションのオブジェクト構造の「鳥瞰図」と呼ぶことができます。これにより、JavaScript オブジェクトと同様に、VM オブジェクト内の関数の内部を確認できるため、アプリケーションのメモリ使用量を非常に低レベルで把握できます。

    このビューには、いくつかのエントリ ポイントが用意されています:
  3. DOMWindow オブジェクト
- これらのオブジェクトは JavaScript コードの「グローバル」オブジェクトです。

GC Root

- VM のガベージ コレクターの実際の G​​C ルートです。

ネイティブ オブジェクト

- ブラウザ オブジェクトは JavaScript 仮想マシンに「プッシュ」され、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;
    }

例: メモリ内のクロージャの影響を分析するために eval が悪である理由をこの例で試してください。ヒープ割り当てをログに記録する次の例を試してみることにも興味があるかもしれません。

DOM メモリ リークを公開する

このツールのユニークな点は、ブラウザーのネイティブ オブジェクト (DOM ノード、CSS ルール) と JavaScript オブジェクト間の双方向参照を表示することです。これは、空き 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 を参照するため、#tree で表されるツリー構造全体は、leafRef が無効化された場合にのみ GC によってリサイクルされます。

例: DOM ノードがメモリをリークする場所とその場所を特定する方法を理解するために、DOM ノードをリークしてみてください。次の例も見ることができます: DOM リークが予想よりも大きい。

DOM メモリ リークとメモリ分析の基本について詳しくは、Gonzalo Ruiz de Villa の記事「Chrome DevTools を使用したメモリ リークの検索とデバッグ」を参照してください。

ネイティブ オブジェクトは、概要ビューと包含ビューで見つけやすくなっています - 独自のカテゴリがあります:

例: DOM ツリーを分離する方法を学ぶには、この例 (新しいタブで開く) を試してください。

ドミネーター ビュー

ドミネーター ビューには、スタック グラフのドミネーター ツリーが表示されます。 Dominator ビューは Containment ビューに似ていますが、プロパティ名がありません。これは、ルーラーが直接参照を持たないオブジェクトである可能性があるためです。これは、ルーラー ツリーがヒープ グラフのスパニング ツリーではないことを意味します。ただし、これはメモリ増加ポイントをすばやく特定するのに役立つ便利なビューです。

注: Chrome Canary では、DevTools の [設定] > [高度なヒープ スナップショット プロパティを表示] でドミネーター ビューをオンにできます。これは、DevTools を再起動した後に有効になります。

例: この例 (新しいタブで開きます) を試して、メモリ増加ポイントを見つける方法を練習してください。パスとドミネーターを保持する次の例をさらに試してみることができます。

オブジェクト割り当てトラッカー

オブジェクト トラッカーは、ヒープ プロファイラーのスナップショット増分更新分析とタイムライン パネルの記録を統合します。他のツールと同様に、オブジェクトのヒープ構成を記録するには、記録を開始し、一連の操作を実行し、記録を停止して分析する必要があります。

オブジェクト トラッカーはヒープ スナップショットを継続的に記録し (頻度は 50 ミリ秒ごとに達します!)、最後に最後のスナップショットを記録します。このヒープ割り当てアナライザーは、オブジェクトが作成された場所とその予約されたパスを示します。

オブジェクト アナライザーを開いて使用します

オブジェクト アナライザーの使用を開始するには: 1. 最新バージョンの Chrome Canary を使用していることを確認します。

  1. DeveTools を開き、歯車アイコンをクリックします (翻訳者: このステップの使用法がわかりません)。

  2. ここで、プロファイラーパネルを開くと、「ヒープ割り当ての記録」オプションが表示されます。

上のバーは、ヒープ内に生成された新しいオブジェクトを表します。高さは対応するオブジェクトのサイズに対応し、その色はそのオブジェクトが最後に撮影されたスナップショットにまだ存在しているかどうかを示します。青い列はオブジェクトがタイムラインの最後にまだ存在していることを示し、灰色の列はそのオブジェクトがまだ存在していることを示します。オブジェクトはタイムラインで生成されましたが、終了する前にメモリが再利用されました。

上記の例では、アクションが 10 回実行されます。同じプログラムは 5 つのオブジェクトを保持するため、最後の 5 つの青いバーが保持されます。しかし、最後に残った列には潜在的な問題があります。タイムライン上のスライダーを使用して、特定のスナップショットを拡大し、割り当てられたオブジェクトを見つけることができます。

ヒープ内のオブジェクトをクリックすると、ヒープ スナップショットの下部に予約済みメモリ ツリーの合計が表示されます。このオブジェクトの予約済みメモリ ツリーの合計を調べると、このオブジェクトがリサイクルされなかった理由を理解するのに十分な情報が得られるため、コードに適切な変更を加えて不要な参照を削除できます。

メモリ分析 FAQ

Q: オブジェクトのすべてのプロパティが表示されません。文字列以外の値も表示されます。なぜ?

すべてのプロパティが JavaScript ヒープに完全に保存されるわけではありません。それらの一部は、ネイティブ コードのゲッター メソッドを実行することによって取得されます。これらのプロパティは、ゲッターが「純粋な」関数でない場合にゲッターの呼び出しを防止し、プログラムの 状態 への変更を回避するために、ヒープ スナップショットにはキャプチャされません。同様に、数値などの非文字列値は、スナップショットのサイズを削減するためにキャプチャされません。

Q:

@ 記号の後の数字は何を意味しますか?アドレスですか、それとも ID ですか?この ID 値は本当に一意ですか? これはオブジェクトIDです。オブジェクトはガベージ コレクション中に削除されるため、オブジェクトのアドレスを表示しても意味がありません。これらのオブジェクト ID は実際の ID です。つまり、異なるスナップショット間で一意に表されます。これにより、ヒープ状態間の正確な比較が可能になります。これらの ID を維持すると、GC プロセスに追加のオーバーヘッドが追加されますが、これは最初のヒープ スナップショットが記録されるときにのみ割り当てられます。ヒープ アナライザーが使用されない場合、追加のオーバーヘッドはありません。

Q: 「死んだ」(参照されていない) オブジェクトはスナップショットに含まれますか?

いいえ、参照できるオブジェクトのみがスナップショットに表示されます。さらに、スナップショットを作成する前に、GC 操作が自動的に実行されます。

注: この記事の執筆時点では、ヒープ サイズの削減を防ぐために、スナップショットを取得するときに GC を実行しない予定です。これは現在では当てはまりますが、ガベージ オブジェクトは依然としてスナップショットの外側に表示されます。

Q: GC ルートは何で構成されていますか?

は、

    ネイティブオブジェクトグラフ、
  • VMスレッドのスタック、
  • の多くの部分によってグループ化されています。
  • コントローラーコンテキスト ;
  • グローバルコントローラー。
  • Q: ヒープ プロファイラーとタイムライン メモリ ビューを使用してメモリ リークを検出できることを学びました。しかし、最初にどのツールを使用すればよいでしょうか?

タイムライン パネルは、ページを初めて使用して速度が低下した場合に、過剰なメモリ使用量を診断するために使用されます。 Web サイトが遅いのはメモリ リークの典型的な兆候ですが、他の理由による場合もあります。レンダリングやネットワークのボトルネックがある可能性があるため、ページの実際の問題に必ず対処してください。

メモリの問題かどうかを判断するには、タイムラインパネルとメモリタブを開きます。記録ボタンをクリックし、メモリ リークを引き起こす可能性があると思われるアプリケーションの操作を数回繰り返します。録音を停止します。アプリケーションのメモリ使用量グラフが生成されます。メモリ使用量が (対応する減少なしに) 増加し続ける場合、これはアプリケーションにメモリ リークが発生している可能性があることを示しています。 一般に、通常のアプリケーションのメモリ使用量グラフはギザギザになります。これは、メモリが使用後にガベージ コレクターによって再利用されるためです。このエイリアスについては心配する必要はありません。JavaScript によるメモリ消費が常にあり、空の requestAnimationFrame であってもこのエイリアスが発生しますが、これは避けられません。大量のメモリを継続的に割り当てる形状でない限り、メモリのゴミが大量に発生することになります。

写真の中の成長上記のラインには注意が必要です。 Memory タグの DOM ノード カウンター、ドキュメント カウンター、およびイベント リスナーの数も、診断分析中に非常に役立ちます。 DOM ノードの数は使用されるネイティブ メモリであり、JavaScript メモリ マップには影響しません。

requestAnimationFrame也会造成这种锯齿形,这是无法避免的。只要不是那种分配了持续很多内存的形状,那就表明生成了很多内存垃圾。

上图的增长线是需要你警惕的。在诊断分析的时候Memory标签中的DOM node counter,Document counter和Event listener count也是很有用的。DOM节点数是使用的原生内存不会影响JavaScript内存图。

一旦你确认你的应用有内存泄漏,堆分析仪就可以用来找到内存泄漏的地方。

问:我发现堆快照中有的DOM节点的数字是用红色标记为”Detached DOM tree”,而其它的是黄色的,这是什么意思呢?

你会发现有不同的颜色。红色的节点(有着深色的背景)没有从JavaScript到它们的直接的引用,但它们是分离出来的DOM结构的一部分,所以他们还是在内存中保留了。有可能有一个节点被JavaScript引用到了(可能是在闭包中或者一个变量),这个引用会阻止整个DOM树被内存回收。

黄色节点(黄色背景)有JavaScript的直接引用。在同一个分离的DOM树中查看一个黄色的节点来定位你的JavaScript的引用。就可能看到从DOM window到那个节点的属性引用链(如:window.foo.bar[2].baz

確認したらアプリケーションにメモリ リークがある場合は、ヒープ アナライザを使用してメモリ リークを見つけることができます。

質問: ヒープ スナップショット内の一部の DOM ノードの番号が「分離された DOM ツリー」として赤色でマークされ、他のものは黄色でマークされていることがわかりました。これは何を意味しますか?

さまざまな色があることがわかります。赤いノード (背景が暗い) は JavaScript から直接参照されていませんが、別の DOM 構造の一部であるため、メモリ内に残ります。ノードが JavaScript によって (おそらくクロージャまたは変数内で) 参照されている可能性があり、この参照により DOM ツリー全体が再利用されなくなります。

黄色ノード (黄色)背景) には JavaScript への直接参照があります。同じ分離された DOM ツリー内で黄色のノードを探して、JavaScript 参照を見つけます。 DOM ウィンドウからそのノードへの属性参照チェーン (window.foo.bar[2].baz など) を確認できます。 次の動的図は、切り離されたノードのプロセスを示しています:

例🎜: この例の切り離されたノードを試してください。タイムラインでノードのライフサイクルを表示し、ヒープ スナップショットを取得して切り離されたノードを見つけることができます。ノード。 🎜🎜🎜Q: 直接占有メモリ (浅いサイズ) と占有合計メモリ (保持サイズ) はそれぞれ何を意味しますか?また、それらの違いは何ですか? 🎜🎜🎜これがケースです。オブジェクトは 2 つの方法 (生きている状態) でメモリ内に存在できます - 別のアクセス可能な (生きている) オブジェクトによって直接保持される (ウィンドウ オブジェクトやドキュメント オブジェクトは常にアクセス可能)、またはネイティブ オブジェクト (DOM オブジェクトのような) 暗黙的な包含によって保持されます。参考文献。後者の方法では、オブジェクトが GC によって自動的にリサイクルされなくなるため、メモリ リークが発生する可能性があります。オブジェクト自体が占有するメモリはダイレクト メモリと呼ばれます (一般的に、配列と文字列はより多くのダイレクト メモリ (浅いサイズ) を予約します)。 🎜🎜🎜🎜

他のオブジェクトが再利用されるのを防ぐことで、任意のサイズのオブジェクトで大量のメモリ使用量を維持できます。オブジェクトが削除されると (オブジェクトが作成する依存関係の一部は参照できなくなります)、解放できるメモリの量は、占有メモリの合計 (保持サイズ) と呼ばれます。

Q: コンストラクターと保持フィールドの下に大量のデータがあります。メモリ リークが発生しているかどうかを調査するにはどこから始めればよいですか?

一般的に、リテイナーでソートされた最初のオブジェクトから開始するのが最善です。リテイナーは距離 (ウィンドウ オブジェクトまでの距離を参照) でソートされます。

距離が最も短いオブジェクトが、メモリ リークを引き起こす可能性がある優先オブジェクトである可能性があります。

Q: ビューの概要、比較、ドミネーター、および包含の違いは何ですか?

ビューを切り替えることで違いを体験できます。

  • [概要] ビューは、コンストラクターごとにグループ化されたオブジェクト (およびそのメモリ使用量) を見つけるのに役立ちます。このビューは、DOM メモリ リークを見つけるのに役立ちます。

  • 比較ビューでは、どのオブジェクト メモリが正しく再利用されたかを示すことで、メモリ リークを検索できます。通常、操作の前後で 2 つ (またはそれ以上) のメモリ使用量のスナップショットが記録されます。解放されたメモリと参照数の差を見てメモリリークが発生しているかどうかを確認し、原因を見つけます。

  • Containment (Control) ビューでは、オブジェクト構造がより適切に表示され、グローバル スコープ (ウィンドウなど) 内のオブジェクト参照を分析して、これらのオブジェクトが保持されているものを見つけるのに役立ちます。これにより、クロージャを分析し、オブジェクトをより深く掘り下げることができます。

  • ドミネーター ビューは、特定の場所 (参照されているオブジェクトなど) に冗長なオブジェクトがまだぶら下がっていないことを確認し、オブジェクトの削除/ガベージ コレクションが実際に機能することを確認するのに役立ちます。

Q: ヒープ アナライザーのコンストラクター (グループ) の内容は何を表しますか?

  • (グローバルプロパティ) - グローバルオブジェクト(「ウィンドウ」など)とそれを参照するオブジェクトとの間の中間オブジェクト。オブジェクトがコンストラクター Person によって生成され、グローバル オブジェクトによって参照される場合、参照パスは次のようになります。これは、相互に直接参照するオブジェクトとは異なります。パフォーマンス上の理由から中間オブジェクトを使用します。グローバル オブジェクトは頻繁に変更されるため、非グローバル変数の属性アクセスの最適化はグローバル変数には適用されません。

  • (roots) - コンストラクター内のルートの内容は、選択したオブジェクトを参照します。エンジンによって自律的に作成された参照であることもあります。このエンジンには参照オブジェクトのキャッシュがありますが、これらの参照は参照オブジェクトのリサイクルを妨げないため、真の強参照 (FIXME) ではありません。

  • (closure) - 一部の関数クロージャ内のオブジェクトのセットへの参照

  • (array, string,number, regexp) - Array、String、Number を参照するプロパティのセットまたは 正規表現

  • (コンパイルされたコード)のオブジェクトタイプ - 簡単に言えば、すべてはコンパイルされたコードに関連しています。スクリプトは関数に似ていますが、実際には 3f1c4e4b6b16bbbd69b2ee476dc4f83a の内容に対応します。 SharedFunctionInfo (SFI) は、関数とコンパイルされたコードの間のオブジェクトです。通常、関数にはコンテンツがありますが、SFIS にはコンテンツがありません (FIXME)。

  • HTMLpElementHTMLAnchorElementDocumentFragmentなど – コード内の要素またはドキュメントオブジェクトへの参照。

イベント リスナーやカスタム オブジェクトなど、プログラムのライフ サイクル中に生成される他の多くのオブジェクトは、次のコントローラーで見つけることができます:

Q: メモリ分析を行っています。影響を与える可能性のある Chrome の機能をオフにしますか?

メモリ分析に Chrome DevTools を使用する場合は、すべての拡張機能をオフにしてシークレット モードを使用するか、Chrome を開く前にユーザー フォルダーを (--user-data-dir="") に設定することをお勧めします。

コンソール内のアプリケーション、拡張機能、さらにはレコードも分析に影響を与える可能性があります。分析を信頼できるものにしたい場合は、これらを無効にしてください。

最後に書いてある言葉

今日の JavaScript エンジンはすでに強力な機能を備えており、コードによって生成されたメモリのガベージを自動的にリサイクルできます。つまり、できることは限られていますが、私たちのアプリケーションでは論理エラーによるメモリ リークが発生することが依然として証明されています。適切なツールを使用してアプリケーションのボトルネックを見つけ、推測ではなくテストしてください。

ヘルプの例

メモリ リークの診断

この記事では多くの内容について言及しましたが、メモリ関連の問題をテストするための一連の例は依然として役に立ちます。以下に DOM ノードのメモリ リークの例を示します。より複雑なページやアプリケーションをテストする前に、これらの例を試してみるとよいでしょう。例 5: メモリと非表示の

クラス
    e
  • 例 10: パスの保持
  • 例 11: 最後の演習

以上がChrome デベロッパー ツールの JavaScript メモリ分析のグラフィカルな紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。