1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptのメモリ管理を理解する:ガベージコレクションとメモリリークを防ぐための設計思考

Posted at

概要

「遅くなった」「ブラウザが落ちる」「時間が経つとメモリ使用量が膨れ上がる」
こういった問題の多くは、**メモリの解放忘れ(リーク)**によって起こる。

JavaScriptでは手動でメモリ解放を行うことはできない。
代わりに、**ガベージコレクション(GC)**という仕組みが自動的に不要なメモリを解放してくれる。

だがこのGC、完全ではない
正しく動作させるためには、“解放可能な状態”を自分で設計してやる必要がある。

本記事では、JavaScriptのメモリ管理の基本から、実務で発生しがちなメモリリークとその防止戦略までを解説する。


対象環境

JavaScript(ES5〜ES2023)  
ブラウザ / Node.js 両対応

ガベージコレクション(GC)の概要

✅ 基本原則:参照されなくなったメモリは自動的に回収される

GCのアルゴリズム(主要ブラウザ実装):

  • Mark-and-Sweep(マーク&スイープ)
    1. ルート(グローバル変数・コールスタックなど)から到達可能なオブジェクトを「マーク」
    2. それ以外の「到達不能なオブジェクト」をメモリからスイープ(除去)

到達可能性の例

let obj = {
  child: {
    name: 'toto'
  }
};

obj = null;

obj.child も到達不能になる → GCによって回収される


なぜメモリリークが起きるのか?

GCは「参照がある限りは解放しない」ため、
意図しない参照が残っていると、使っていないのにメモリを保持し続けることになる。


よくあるメモリリークのパターンと対策

1. グローバル変数に値を残す

window.leak = createLargeObject(); // ❌ GCされない

→ ✅ 関数スコープ内で使い切る、グローバル汚染を避ける


2. クロージャ内に不要な値を保持

function leak() {
  let large = new Array(1000000).fill('...');
  return () => console.log(large.length);
}
const f = leak();

large はずっと生き続ける

→ ✅ クロージャを使う場合は、本当に必要な変数だけを閉じ込める


3. イベントリスナーの解除忘れ

const el = document.getElementById('btn');
el.addEventListener('click', () => {
  // リスナーがelを保持し続ける
});

→ ✅ removeEventListener を明示的に呼ぶ、もしくは AbortController を使う


4. タイマーやintervalが残り続ける

setInterval(() => {
  // 無限ループで実行され続ける
}, 1000);

→ ✅ clearInterval() をコンポーネント破棄時に行う(SPAで重要)


5. DOM参照を閉じ込める

const nodes = [];
document.querySelectorAll('*').forEach(node => nodes.push(node));

→ ✅ 必要なDOMだけをキャッシュし、不要になったら null に


ツールで可視化する:Chrome DevTools編

  1. DevTools → [Memory] タブを開く
  2. 「Take snapshot」でヒープスナップショットを撮影
  3. オブジェクトグループを比較し、Detached DOM treelistener を確認
  4. メモリが減っていない=リークの兆候

WeakMap / WeakSet による安全な一時保持

const cache = new WeakMap();

function cacheData(obj, data) {
  cache.set(obj, data);
}

→ ✅ GC対象となるオブジェクトをキーにすれば、参照が切れると自動解放


状態管理やフレームワークにおける注意

  • React / Vue のコンポーネントでは、useEffectのクリーンアップwatchの解除が不可欠
  • グローバルストアに巨大データを保持し続ける設計は危険

結語

JavaScriptのメモリは、自動管理されている。
だが「自動だから任せていい」わけではない。
任せられる状態を、意図して設計することが求められる。

  • 使い終わった値は開放できる構造か
  • イベントやクロージャに意図しない参照はないか
  • フレームワークの裏で何が残っているのか

目に見えない世界を制御するには、見える構造をコードとして設計するしかない。

メモリ管理とは、パフォーマンスチューニングではない。
持続可能なアプリケーション設計の基礎である。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?