7.2 Memoryパネル
Memory Panel
Memoryパネルは、JavaScriptのメモリ使用状況を分析するツール。メモリリーク、過剰なメモリ消費、ガベージコレクションの問題を特定できる。長時間動作するWebアプリケーションの安定性向上に不可欠。
Memoryパネルの概要
パネルを開く
F12 → Memoryタブ
プロファイルの種類
| 種類 | 用途 |
|---|---|
| Heap snapshot | 特定時点のメモリ状態を撮影。オブジェクトの詳細を分析 |
| Allocation instrumentation on timeline | 時系列でメモリ割り当てを記録。リークの原因を追跡 |
| Allocation sampling | サンプリングベースで軽量に記録。長時間の監視向け |
Heap Snapshot(ヒープスナップショット)
特定時点でのJavaScriptヒープメモリの完全なスナップショットを取得する。
スナップショットの取得
操作手順
- Memoryパネルを開く
- 「Heap snapshot」を選択
- 「Take snapshot」をクリック
- スナップショットが左サイドバーに追加される
ビューの種類
| ビュー | 説明 |
|---|---|
| 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を取得
- リークが疑われる操作を実行
- 操作後にスナップショット2を取得
- スナップショット2を選択
- ビューを「Comparison」に変更
- 比較対象としてスナップショット1を選択
比較ビューの列
| 列 | 説明 |
|---|---|
| # New | 新規に作成されたオブジェクト数 |
| # Deleted | 削除されたオブジェクト数 |
| # Delta | 差分(New - Deleted) |
| Alloc. Size | 新規割り当てサイズ |
| Freed Size | 解放されたサイズ |
| Size Delta | サイズの差分 |
リーク検出のコツ
同じ操作を複数回繰り返し、毎回スナップショットを取得。#Deltaが増え続けるオブジェクトがリークの候補。
Allocation Timeline
時系列でメモリ割り当てを記録し、いつ・どこでオブジェクトが作成されたかを追跡する。
記録の手順
操作手順
- 「Allocation instrumentation on timeline」を選択
- 「Start」をクリック
- 分析したい操作を実行
- 「Stop」をクリック
タイムラインの読み方
- 青いバー: 割り当てられ、まだ存在するオブジェクト
- グレーのバー: 割り当て後、GCで回収されたオブジェクト
範囲の選択
タイムライン上で範囲をドラッグすると、その期間に割り当てられたオブジェクトだけを表示できる。特定の操作に関連するオブジェクトを絞り込むのに便利。
Allocation Sampling
サンプリングベースの軽量なプロファイリング。長時間の監視に適している。
特徴
- オーバーヘッドが小さい
- 本番環境に近い状態で計測可能
- 統計的なデータ(すべてのオブジェクトは記録されない)
ビュー
| ビュー | 説明 |
|---|---|
| Chart | 時系列グラフ |
| Heavy (Bottom Up) | 割り当てが多い関数から順に表示 |
| Tree (Top Down) | コールスタックをツリー形式で表示 |
Detached DOM要素の検出
DOMから切り離されたがJavaScriptから参照されている要素。よくあるメモリリークの原因。
検出方法
操作手順
- ヒープスナップショットを取得
- フィルターに「Detached」と入力
- 「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
[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