measureUserAgentSpecificMemory() を使用してウェブページのメモリの合計使用量をモニタリングする

本番環境のウェブページのメモリ使用量を測定して、回帰を検出する方法を学びます。

Brendan Kenny
Brendan Kenny
Ulan Degenbaev
Ulan Degenbaev

ブラウザはウェブページのメモリを自動的に管理します。ウェブページが オブジェクトを作成すると、ブラウザは「内部で」メモリのチャンクを割り当てます。から 保存します。メモリは有限のリソースなので、ブラウザは オブジェクトが不要になったことを検出して解放するためのガベージ コレクション メモリチャンクに分割されます

しかし検出は完璧ではなく あることが実証されています 不可能ですしたがって、ブラウザでは「オブジェクト」という概念に 必要だ」これは「オブジェクトに到達可能」ですウェブページが 他の到達可能なオブジェクトの変数やフィールドを介して オブジェクトに到達できます ブラウザはオブジェクトを安全に再利用できます。両者の違いは 2 つの概念がある場合、次の例に示すようにメモリリークにつながります。

const object = {a: new Array(1000), b: new Array(2000)};
setInterval(() => console.log(object.a), 1000);

ここでは、大きい配列 b は不要になりましたが、ブラウザでは必要ありません コールバックで object.b 経由で引き続きアクセスできるため、再利用します。したがって 大きな配列のメモリリークが発生します

メモリリークはウェブでよくある問題です。 イベント リスナーの登録解除を忘れた場合に、次のような方法で簡単に導入できます。 ワーカーを閉じないことで誤って iframe からオブジェクトをキャプチャする。 配列内のオブジェクトの蓄積などですウェブページにメモリリークがある場合は 時間の経過とともにメモリ使用量が増え ウェブページの表示が遅くなり ユーザーに肥大化させています

この問題を解決するための第一歩は、測定です。新しい performance.measureUserAgentSpecificMemory() API を使用すると、デベロッパーは 本番環境のウェブページのメモリ使用量を測定して、メモリの 漏れなく改善できます。

performance.measureUserAgentSpecificMemory() と以前の performance.memory API の違い

既存の非標準の performance.memory API に精通している場合は、 新しい API と何が違うのか疑問に思われるかもしれません。主な違いは 古い API では JavaScript ヒープのサイズが返されるのに対し、新しい API では ウェブページで使用されているメモリを推定します。この違いにより、 Chrome が複数のウェブページ(または (同じウェブページの複数のインスタンスがある場合など)?そのような場合、古いバージョンの API は任意に無効にできます。古い API は 実装固有の用語(「ヒープ」など)があり、それを標準化するのは望ましくありません。

もう一つの違いとして、新しい API では、サーバーの使用中にメモリが 使用されますこれにより、結果のノイズが軽減されますが、 結果が生成されます。なお、他のブラウザは ガベージ コレクションに依存せずに新しい API を実装できます。

推奨されるユースケース

ウェブページのメモリ使用量は、イベントのタイミング、ユーザーの操作、 使用されますそのため、Memory Measurement API は 本番環境のメモリ使用量を集計できます個々の通話の結果 有用性は低くなります。ユースケースの例:

  • 新しいメモリリークを検出するために、ウェブページの新しいバージョンをロールアウトする際の回帰検出。
  • メモリへの影響を評価し、メモリリークを検出する新機能の A/B テスト。
  • メモリ使用量とセッション継続時間を関連付けて、メモリリークの有無を検証する。
  • メモリ使用量をユーザー指標と関連付けて、メモリ使用量の全体的な影響を把握します。

ブラウザの互換性

対応ブラウザ

  • Chrome: 89。 <ph type="x-smartling-placeholder">
  • Edge: 89。 <ph type="x-smartling-placeholder">
  • Firefox: サポートされていません。 <ph type="x-smartling-placeholder">
  • Safari: サポートされていません。 <ph type="x-smartling-placeholder">

ソース

現在、この API は Chrome 89 以降、Chromium ベースのブラウザでのみサポートされています。「 API の結果は実装に大きく依存します。これは、ブラウザによって メモリ内のオブジェクトを表すさまざまな方法や、 メモリ使用量を見積もることができますブラウザによっては、一部のメモリ領域が 適切なアカウンティングがコストがかかりすぎるか実現不可能な場合にしたがって、結果は ブラウザ間で比較することはできません。前のバージョンと 表示することもできます。

performance.measureUserAgentSpecificMemory() の使用

機能検出

performance.measureUserAgentSpecificMemory 関数は使用できなくなるか、 実行環境が要件を満たさない場合、SecurityError で失敗します。 クロスオリジンの情報漏洩を防ぐためのセキュリティ要件も規定しています。 ウェブページでクロスオリジン分離を利用できる COOP+COEP ヘッダーを設定すると、

サポートは実行時に検出できます。

if (!window.crossOriginIsolated) {
  console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
} else if (!performance.measureUserAgentSpecificMemory) {
  console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
} else {
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
    } else {
      throw error;
    }
  }
  console.log(result);
}

ローカルテスト

Chrome はガベージ コレクションの際にメモリ測定を実行します。つまり、 API は結果の Promise をすぐには解決せず、待機する 次のガベージ コレクションに対して設定します。

API を呼び出すと、一定のタイムアウト後にガベージ コレクションが実行されます。 20 秒に設定されていますが、それより早く設定される可能性もあります。で Chrome を起動する --enable-blink-features='ForceEagerMeasureMemory' コマンドライン フラグの削減 これは、ローカルでのデバッグやテストに役立ちます。

API の推奨される使用方法は、グローバル メモリ モニターを定義することです。 ウェブページ全体のメモリ使用量をサンプリングし、その結果をサーバーに送信する 集約や分析に使用できます。最も簡単な方法は、定期的にサンプリングし、 M 分ごとに行われます。ただし この方法ではデータにバイアスが入ります。 サンプル間にメモリのピークが発生することがあります。

次の例で、 ポアソン法を使ってバイアスのないメモリ測定を行う 任意の時点でサンプルが発生する可能性が同等であることを保証 (デモ出典)。

まず、次のコマンドを使用して、次回のメモリ測定をスケジュールする関数を定義します。 setTimeout() は、ランダムな間隔に置き換えます。

function scheduleMeasurement() {
  // Check measurement API is available.
  if (!window.crossOriginIsolated) {
    console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
    console.log('See https://web.dev/coop-coep/ to learn more')
    return;
  }
  if (!performance.measureUserAgentSpecificMemory) {
    console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
    return;
  }
  const interval = measurementInterval();
  console.log(`Running next memory measurement in ${Math.round(interval / 1000)} seconds`);
  setTimeout(performMeasurement, interval);
}

measurementInterval() 関数はランダムな間隔をミリ秒単位で計算する 平均して 5 分ごとに 1 回という測定値があります指数関数的 分布関数 を使用します。

function measurementInterval() {
  const MEAN_INTERVAL_IN_MS = 5 * 60 * 1000;
  return -Math.log(Math.random()) * MEAN_INTERVAL_IN_MS;
}

最後に、非同期 performMeasurement() 関数は API を呼び出し、 次の測定をスケジュールします

async function performMeasurement() {
  // 1. Invoke performance.measureUserAgentSpecificMemory().
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
      return;
    }
    // Rethrow other errors.
    throw error;
  }
  // 2. Record the result.
  console.log('Memory usage:', result);
  // 3. Schedule the next measurement.
  scheduleMeasurement();
}

最後に測定を開始します

// Start measurements.
scheduleMeasurement();

結果は次のようになります。

// Console output:
{
  bytes: 60_100_000,
  breakdown: [
    {
      bytes: 40_000_000,
      attribution: [{
        url: 'https://example.com/',
        scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 20_000_000,
      attribution: [{
          url: 'https://example.com/iframe',
          container: {
            id: 'iframe-id-attribute',
            src: '/iframe',
          },
          scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 100_000,
      attribution: [],
      types: ['DOM']
    },
  ],
}

メモリ使用量の合計の見積もりが bytes フィールドに返されます。この値は 実装に大きく依存し、ブラウザ間で比較できません。かもしれない 同じブラウザの異なるバージョン間でも 変更できますこの値には、 すべての iframe、関連ウィンドウ、ウェブワーカーの JavaScript と DOM メモリ 確認できます。

breakdown リストは、使用済みメモリに関する詳細情報を提供します。各 エントリはメモリの一部について説明し、メモリのセット ウィンドウ、iframe、ワーカーを URL で識別します。types フィールドには、 メモリに関連付けられている実装固有のメモリタイプ。

すべてのリストを汎用的な方法で扱い、ハードコードしないことが重要です。 特定のブラウザに基づく推測です。たとえば、ブラウザによっては、 空の breakdown または空の attribution を返します。他のブラウザでは、 区別できなかったことを示す複数のエントリを attribution に返す どちらがメモリを所有するかが決まります

フィードバック

ウェブ パフォーマンス コミュニティ グループ、Chrome チーム一同 自分の考えや体験を聞く performance.measureUserAgentSpecificMemory()

API 設計について教えてください

API に関して想定どおりに機能していないものはありますか?それとも プロパティが足りない場合仕様の問題を報告 performance.measureUserAgentSpecificMemory() GitHub リポジトリを参照するか、 考えをまとめます

実装に関する問題を報告する

Chrome の実装にバグは見つかりましたか?それとも どうなるでしょうかnew.crbug.com でバグを報告します。必ず できるだけ詳細な情報を含め、問題を再現するための [Components] を Blink>PerformanceAPIs に設定します。 Glitch は、すばやく簡単に再現を共有するのに最適です。

サポートの気持ちを伝える

performance.measureUserAgentSpecificMemory() を使用する予定はありますか?公開サポート Chrome チームが機能の優先順位付けに役立ち、他のブラウザ ベンダーに サポートすることが重要です。@ChromiumDev にツイートを送信してください どこで、どのように使用されているかをお知らせください。

関連情報

謝辞

API 設計のレビューに協力してくれた Domenic Denicola、Yoav Weiss、Mathias Bynens に深く感謝します。 Dominik Inführ、Hannes Payer、Kentaro Hara、Michael Lippautz によるコードレビュー できます。Per Parker、Philipp Weis、Olga Belomestnykh、Matthew にも感謝します Bolohan、Neil Mckay に、貴重なユーザー フィードバックを API を改善しました。

ヒーロー画像: Harrison BroadbentUnsplash より