2
2

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)は、「参照が切れた」ことをトリガーに自動解放を行う。
つまり、参照がどこかに残っていれば、永遠に解放されない
この問題を予防・発見・解決するための構造と戦略を、本稿で解説する。


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におけるメモリリーク対策とは、
“見えない領域で破綻を起こさないために、構造そのものに「解放」を織り込む設計戦略”である。

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?