概要
メモリリークとは「メモリが多く使われること」ではない。
それは**“本来解放されるべき参照が保持されたままとなり、永続的にリソースを占有する構造的な不具合”**である。
JavaScriptのガベージコレクション(GC)は、「参照が切れた」ことをトリガーに自動解放を行う。
つまり、参照がどこかに残っていれば、永遠に解放されない。
この問題を予防・発見・解決するための構造と戦略を、本稿で解説する。
1. よくあるメモリリークの原因パターン
① DOMを削除したのにリスナーが残っている
② クロージャで不要な値をキャプチャしている
③ setInterval が clear されない
④ キャッシュ・Map/Setが参照を保持し続ける
⑤ グローバル変数で参照が残る
2. イベントリスナーの解除
const el = document.getElementById('btn');
const handler = () => doSomething();
el.addEventListener('click', handler);
// ✅ 破棄時に解除
el.removeEventListener('click', handler);
- ✅ UIコンポーネントのライフサイクルに応じて 明示的な remove
- ✅ 匿名関数のままでは remove できないので注意
3. setInterval / setTimeout の解放忘れ
const id = setInterval(() => {
update();
}, 1000);
// ✅ コンポーネント破棄時に必ず clear
clearInterval(id);
- ✅
setInterval
は 永続的にメモリを占有しがち - ✅
setTimeout
も複数が蓄積しないよう注意
4. クロージャで外部値を不要に捕まえない
function createHandler() {
const largeData = getHeavyData(); // 重いデータ
return function handler() {
// 実際には使っていないがクロージャでキャプチャされる
console.log('clicked');
};
}
- ❌
largeData
は使っていないのに 参照が保持され続ける - ✅ クロージャ内で使わない変数は 持ち込まない設計
5. Map / Set による参照保持と WeakMap の使い分け
const cache = new Map();
cache.set(domElement, someData); // domElementを永続保持してしまう
// ✅ WeakMap を使えば、GC対象になれば自動で解放される
const weakCache = new WeakMap();
weakCache.set(domElement, someData);
- ✅ オブジェクトをキーにする場合、WeakMap/WeakSetを優先
6. ライブラリ / フレームワークとの連携(React / Vue)
React: useEffect内の副作用破棄
useEffect(() => {
const id = setInterval(fetch, 1000);
return () => clearInterval(id);
}, []);
Vue: onUnmounted
で後処理登録
onMounted(() => {
const id = setInterval(...);
onUnmounted(() => clearInterval(id));
});
- ✅ フック内で 「始めたら終わらせる」構造をセットで書く
設計判断フロー
① DOMやイベントは「解除処理」が設計されているか?
② クロージャでキャプチャしている値は本当に必要か?
③ setInterval / setTimeout はライフサイクルに紐づいているか?
④ キャッシュ構造に WeakMap を使えるか?
⑤ 状態の永続保持を意図していないのに参照が残っていないか?
よくあるミスと対策
❌ イベントを add するだけで remove を忘れる
→ ✅ イベント登録関数には remove を返す設計に
❌ モーダルが開かれるたびに setInterval が増殖する
→ ✅ 初期化と破棄を1対1で設計
❌ キャッシュ用の Map に DOM をキーにしてしまう
→ ✅ 自動解放されないので WeakMap に切り替え
結語
メモリリークとは「放置してたら重くなった」ではない。
それは**“構造的に不要な参照を保持し、解放の設計が破綻している状態”**である。
- イベント、タイマー、クロージャは開始と同時に終了設計を持ち
- Map/Setの代替として Weak構造を選択し
- フレームワークのライフサイクルと合わせて設計し
- 参照の「終わり」を設計に含める
JavaScriptにおけるメモリリーク対策とは、
“見えない領域で破綻を起こさないために、構造そのものに「解放」を織り込む設計戦略”である。