【Node.js】メモリリークの原因と検出方法|heapdumpとprofilerで可視化

【Node.js】メモリリークの原因と検出方法|heapdumpとprofilerで可視化 Node.js

Node.jsは非同期で高性能なアプリケーション開発に適していますが、運用中に「メモリ使用量がじわじわと増加する」「一定時間後にサーバーが落ちる」といった現象が発生することがあります。こうした現象の多くは、メモリリークに起因します。

この記事では、Node.jsにおけるメモリリークの典型的な原因と、それを検出・解析するためのツールとしてheapdumpChrome DevToolsのprofilerを使った実践的な対処法を紹介します。

Node.jsでよくあるメモリリークの原因

メモリリークとは、不要になったオブジェクトがガベージコレクションされずにメモリ上に残り続ける現象です。以下のようなコードパターンで発生します。

1. グローバル変数やクロージャによる参照の残存

let cache = [];

function fetchData() {
  cache.push(new Array(1000000).fill('data')); // 毎回追加され、解放されない
}

2. イベントリスナーの登録解除漏れ

function setup(ws) {
  ws.on('message', handleMessage); // 接続が切れてもリスナーが残るとリーク
}

3. マップや配列に不要なデータを保持し続ける

const sessionStore = new Map();

function handleRequest(userId) {
  sessionStore.set(userId, { timestamp: Date.now() });
}

上記のように、意図しない参照の保持や、明示的な削除忘れがメモリリークの主因です。

heapdumpを使ってヒープスナップショットを取得

heapdumpは、実行中のNode.jsプロセスのヒープ(メモリ領域)の状態をファイルに出力できるパッケージです。

インストール

npm install heapdump

スナップショットの出力コード

const heapdump = require('heapdump');
const express = require('express');
const app = express();

app.get('/snapshot', (req, res) => {
  const filename = `heap-${Date.now()}.heapsnapshot`;
  heapdump.writeSnapshot(filename, (err, filename) => {
    res.send(`Heap snapshot written to ${filename}`);
  });
});

app.listen(3000);

上記のようにエンドポイントを作成することで、任意のタイミングでスナップショットを取得できます。

Chrome DevToolsでスナップショットを可視化

取得した.heapsnapshotファイルは、ChromeのDevToolsから読み込むことで解析可能です。

  1. Chromeを開く
  2. 開発者ツール → Memoryタブ
  3. 「Load」ボタンからスナップショットを読み込み

「Retainers」や「Object Types」を見ながら、どのオブジェクトが不要に残っているかを調査します。

profilerを使ったリアルタイム観測

Node.jsは--inspectオプションで起動することで、Chromeと接続してリアルタイムにパフォーマンスプロファイルを取得できます。

起動方法

node --inspect index.js

Chromeでchrome://inspectにアクセスすると、接続されたプロセスを選択できます。

DevToolsのMemoryタブで「Record Allocation Timeline」や「Heap Snapshot」を活用することで、どのタイミングでメモリ使用量が増加しているのかを特定可能です。

その他の便利ツール

  • clinic.js(npm i -g clinic:性能やメモリ使用量を統合的に可視化
  • v8-profiler-next:カスタムプロファイルを取得して分析

まとめ|Node.jsでもメモリ管理は油断禁物

Node.jsは非同期で効率的に動作する一方で、JavaScriptならではのスコープやクロージャの罠により、気づかぬうちにメモリを食いつぶすケースがあります。

本番環境では、heapdump--inspectを活用した定点観測と可視化が不可欠です。定期的にスナップショットを取得して比較することで、潜在的なリークの兆候を早期に発見できるようになります。

パフォーマンス維持と安定稼働のためにも、メモリリークへの意識を高く持っておきましょう。