なぜ、あなたのアプリは時間が経つほど「重く」なるのか?
大規模なSPA(Single Page Application)を開発していると、必ず直面する壁があります。それは「メモリリーク」。最初はサクサク動いていたはずのアプリが、画面遷移を繰り返すうちに重くなり、最終的にはブラウザがクラッシュする……。
多くのエンジニアはこれを「ブラウザのせい」や「フレームワークのせい」にしがちですが、実はその犯人は、あなたのコードが残した「忘れ物」である可能性が極めて高いのです。
今日は、ブラウザ開発者ツール(DevTools)の「Heap Snapshot」を使い、メモリを食い荒らす犯人を科学的に特定する「比較分析術」を伝授します。これをマスターすれば、あなたはもう「なんとなくメモリが減らない」という恐怖から解放されます。
—
1. メモリリークの正体:GCの隙を突く「幽霊オブジェクト」
まず、メモリリークの概念を正しく理解しましょう。JavaScriptには「ガベージコレクション(GC)」という自動掃除機能があります。参照されなくなったオブジェクトは、自動的にメモリから解放されます。
しかし、「参照を切ったつもりなのに、どこかで誰かが握っている」状態になると、GCはそれを「まだ使われている重要なデータ」と誤認し、消さずに放置します。これがメモリリークの正体です。
- 削除し忘れたイベントリスナー
- グローバル変数に格納されたDOM要素
- クロージャ内に閉じ込められた巨大なデータ
これらが積み重なると、ヒープメモリ(メモリの保管庫)はパンクします。
—
2. 実践:Heap Snapshotを使った「比較分析」の極意
単にスナップショットを撮るだけでは意味がありません。真髄は「2つの状態を比較すること」にあります。
ステップ1:ベースラインの測定
まずは、アプリを起動した直後、あるいは「メモリが綺麗な状態」で1つ目のスナップショットを撮ります。
1. Chrome DevToolsを開き、`Memory` タブを選択。
2. `Heap snapshot` を選択し、左上の「●(Record)」ボタンをクリック。
3. これが「基準値」となります。
ステップ2:リークを誘発させる
次に、メモリがリークしていると思われる操作を行います。例えば「メニューを開いて閉じる」を5回繰り返すなど、本来ならメモリが元に戻るはずの操作をします。
ステップ3:比較して犯人を炙り出す
操作が終わった状態で、もう一度スナップショットを撮ります。ここで重要なのが、「Comparison(比較)」モードへの切り替えです。
- `Heap snapshot` のドロップダウンから `Comparison` を選択。
- 比較対象として、先ほど撮った「ベースライン」を指定します。
ここで見るべきは `Delta` 列です。「+(プラス)」になっているオブジェクトこそが、操作後に消えずに残っている「幽霊」の正体です。
—
3. HelloWorld的な動作確認:わざとリークを作ってみよう
論より証拠。以下のコードをコンソールに貼り付けて、メモリリークがどう見えるか体験してください。
// 【実験用】わざとDOMをメモリに残すコード
window.leakyArray = []; // グローバル変数で配列を確保
function createLeakyElement() {
const div = document.createElement(‘div’);
div.innerText = “私は消えない幽霊です”;
// 本来なら不要になったら削除されるべきだが、グローバル配列に突っ込む
window.leakyArray.push(div);
}
// 実行するたびにDOMノードがメモリに蓄積される
createLeakyElement();
これを実行した後でスナップショットを比較すると、`Delta` のトップに `HTMLDivElement` が現れるはずです。これこそが、あなたが退治すべき「メモリリークの犯人」です。
—
4. 現場で震えるほど役立つ「深掘り」のテクニック
犯人が `HTMLDivElement` だとわかっても、どのコードが握っているのか分からないことがあります。その時は「Retainers(保持者)」パネルを見てください。
- `Retainers` パネルには、「誰がそのオブジェクトを握っているか」という参照のツリー構造が表示されます。
- ここを辿れば、`window.leakyArray` のような「リークの根源」である変数名が必ず見つかります。
アーキテクトからのアドバイス:GCを手動で叩く
ブラウザのGCは予測不能です。スナップショットを撮る直前に、DevToolsのゴミ箱アイコン(Collect garbage)をクリックしてください。これで「GCで消せるものは消えた状態」を作り出せます。この状態で残っているものこそが、本物のリークです。
—
まとめ:メモリ管理は「開発者の規律」
メモリリークの特定は、いわば「デジタルな鑑識捜査」です。
1. 清潔な状態を記録する。
2. 汚染された状態を記録する。
3. 差分(Delta)から犯人を特定し、Retainersで犯行現場を押さえる。
この手順を習慣化すれば、どれだけ巨大なアプリケーションであっても、メモリの健康を保ち続けることができます。「なんとなく遅い」という不安は、今日で卒業です。
さあ、あなたのアプリの「忘れ物」を、今すぐ探しに行きましょう!