7.2 Memoryパネル

Memory Panel

Memoryパネルは、JavaScriptのメモリ使用状況を分析するツール。メモリリーク、過剰なメモリ消費、ガベージコレクションの問題を特定できる。長時間動作するWebアプリケーションの安定性向上に不可欠。

Memoryパネルの概要

パネルを開く

F12 → Memoryタブ

プロファイルの種類

種類 用途
Heap snapshot 特定時点のメモリ状態を撮影。オブジェクトの詳細を分析
Allocation instrumentation on timeline 時系列でメモリ割り当てを記録。リークの原因を追跡
Allocation sampling サンプリングベースで軽量に記録。長時間の監視向け

Heap Snapshot(ヒープスナップショット)

特定時点でのJavaScriptヒープメモリの完全なスナップショットを取得する。

スナップショットの取得

操作手順

  1. Memoryパネルを開く
  2. 「Heap snapshot」を選択
  3. 「Take snapshot」をクリック
  4. スナップショットが左サイドバーに追加される

ビューの種類

ビュー 説明
Summary コンストラクタ名でグループ化。最も一般的なビュー
Comparison 2つのスナップショットの差分を表示
Containment オブジェクトの包含関係をツリー表示
Statistics メモリ使用量の統計を円グラフで表示

列の意味(Summary)

説明
Constructor オブジェクトのコンストラクタ名
Distance GCルートからの最短距離
Shallow Size オブジェクト自身のサイズ
Retained Size オブジェクトとその参照先を含む合計サイズ
Shallow Size vs Retained Size Shallow Sizeはオブジェクト自身のメモリ。Retained Sizeは、そのオブジェクトが解放されると一緒に解放されるメモリの合計。メモリリークの影響を見るにはRetained Sizeが重要。

スナップショットの比較

メモリリークを検出する最も効果的な方法。

比較の手順

操作手順

  1. 操作前にスナップショット1を取得
  2. リークが疑われる操作を実行
  3. 操作後にスナップショット2を取得
  4. スナップショット2を選択
  5. ビューを「Comparison」に変更
  6. 比較対象としてスナップショット1を選択

比較ビューの列

説明
# New 新規に作成されたオブジェクト数
# Deleted 削除されたオブジェクト数
# Delta 差分(New - Deleted)
Alloc. Size 新規割り当てサイズ
Freed Size 解放されたサイズ
Size Delta サイズの差分
リーク検出のコツ 同じ操作を複数回繰り返し、毎回スナップショットを取得。#Deltaが増え続けるオブジェクトがリークの候補。

Allocation Timeline

時系列でメモリ割り当てを記録し、いつ・どこでオブジェクトが作成されたかを追跡する。

記録の手順

操作手順

  1. 「Allocation instrumentation on timeline」を選択
  2. 「Start」をクリック
  3. 分析したい操作を実行
  4. 「Stop」をクリック

タイムラインの読み方

  • 青いバー: 割り当てられ、まだ存在するオブジェクト
  • グレーのバー: 割り当て後、GCで回収されたオブジェクト

範囲の選択

タイムライン上で範囲をドラッグすると、その期間に割り当てられたオブジェクトだけを表示できる。特定の操作に関連するオブジェクトを絞り込むのに便利。

Allocation Sampling

サンプリングベースの軽量なプロファイリング。長時間の監視に適している。

特徴

  • オーバーヘッドが小さい
  • 本番環境に近い状態で計測可能
  • 統計的なデータ(すべてのオブジェクトは記録されない)

ビュー

ビュー 説明
Chart 時系列グラフ
Heavy (Bottom Up) 割り当てが多い関数から順に表示
Tree (Top Down) コールスタックをツリー形式で表示

Detached DOM要素の検出

DOMから切り離されたがJavaScriptから参照されている要素。よくあるメモリリークの原因。

検出方法

操作手順

  1. ヒープスナップショットを取得
  2. フィルターに「Detached」と入力
  3. 「Detached HTMLDivElement」等が表示される

よくある原因

// 悪い例:イベントリスナーがDOM要素への参照を保持
const button = document.getElementById('myButton');
button.addEventListener('click', function handler() {
  console.log('clicked');
});
button.remove(); // DOMから削除してもhandlerが参照を保持

// 良い例:削除前にイベントリスナーを解除
button.removeEventListener('click', handler);
button.remove();

Retainersパネル

オブジェクトを選択すると、下部のRetainersパネルに「何がそのオブジェクトを参照しているか」が表示される。リークの原因を追跡するのに使用。

よくあるメモリリークパターン

1. グローバル変数

// 悪い例:意図しないグローバル変数
function leak() {
  leaked = []; // varがないのでグローバル
  for (let i = 0; i < 1000000; i++) {
    leaked.push(new Array(1000));
  }
}

// 良い例:ローカル変数として宣言
function noLeak() {
  const local = [];
  // ...
}

2. クロージャ

// 悪い例:クロージャが大きなデータを保持
function createClosure() {
  const largeData = new Array(1000000);
  return function() {
    console.log(largeData.length); // largeDataへの参照を保持
  };
}

// 良い例:必要な情報だけを保持
function createClosure() {
  const largeData = new Array(1000000);
  const length = largeData.length;
  return function() {
    console.log(length);
  };
}

3. タイマー

// 悪い例:クリアされないタイマー
function startTimer() {
  setInterval(() => {
    // 何かの処理
  }, 1000);
}

// 良い例:タイマーをクリア
let timerId;
function startTimer() {
  timerId = setInterval(() => { /* ... */ }, 1000);
}
function stopTimer() {
  clearInterval(timerId);
}

4. イベントリスナー

// 悪い例:解除されないリスナー
window.addEventListener('resize', handleResize);

// 良い例:コンポーネント破棄時に解除
function cleanup() {
  window.removeEventListener('resize', handleResize);
}

5. Map/Setでのオブジェクトキー

// 悪い例:Mapがオブジェクトへの参照を保持
const cache = new Map();
function process(obj) {
  cache.set(obj, computeExpensiveValue(obj));
}

// 良い例:WeakMapを使用(GC可能)
const cache = new WeakMap();
function process(obj) {
  cache.set(obj, computeExpensiveValue(obj));
}

ガベージコレクション

手動でGCを実行

Memoryパネルのゴミ箱アイコン(Collect garbage)をクリック。スナップショット取得前に実行すると、より正確な状態を確認できる。

GCの確認

Performanceパネルで記録中、「Minor GC」「Major GC」のイベントを確認できる。頻繁なGCはパフォーマンスに影響する。

GCの仕組み JavaScriptエンジンは参照されなくなったオブジェクトを自動的に解放する。ただし、参照が残っている限り解放されない。これがメモリリークの原因となる。

Performance Monitor

リアルタイムでメモリ使用量を監視するツール。

開き方

Ctrl+Shift+P → 「Show Performance Monitor」

監視できる項目

項目 説明
JS heap size JavaScriptヒープの使用量
DOM Nodes DOM要素数
JS event listeners イベントリスナー数
Documents ドキュメント数(iframe含む)
Frames フレーム数
Layouts / sec 1秒あたりのレイアウト回数
Style recalcs / sec 1秒あたりのスタイル再計算回数
リーク検出の簡易チェック Performance Monitorで「JS heap size」を監視しながら操作を繰り返す。GC後も数値が上昇し続ける場合、メモリリークの可能性がある。
参考文献
[1] Fix memory problems - Chrome DevTools. developer.chrome.com
[2] Memory terminology - Chrome DevTools. developer.chrome.com
[3] Record heap snapshots - Chrome DevTools. developer.chrome.com