Node.js --expose-gcでメモリリークをデバッグする

問題

長時間実行されるNode.jsアプリ(MQTTクライアント、バッチプロセッサなど)でメモリが増加し続けます。GCが正常に動作しているのか、本当にリークなのか区別がつきません。

解決方法

# --expose-gcで実行するとglobal.gc()を呼び出せる
node --expose-gc app.js

# Dockerの場合
CMD ["node", "--expose-gc", "--max-old-space-size=512", "dist/index.js"]
function logMemory(label) {
  if (global.gc) global.gc(); // 手動GC実行
  const usage = process.memoryUsage();
  console.log(`[${label}] Heap: ${Math.round(usage.heapUsed / 1024 / 1024)}MB / ${Math.round(usage.heapTotal / 1024 / 1024)}MB`);
}

logMemory('開始');
await processLargeData();
logMemory('処理後');  // GC後もメモリが減らなければ → リーク

ポイント

  • global.gc()を呼び出した後もheapUsedが増加し続ければ、本当のメモリリークです。GCが回収できないオブジェクトがどこかで参照されています。
  • --max-old-space-size=512でヒープサイズを制限すると、リークがある場合にOOMで早くクラッシュするため、問題を早期発見できます。
  • プロダクション環境では--expose-gcを使わないのが良いです。手動GCはパフォーマンスに影響します。デバッグ時のみ使用してください。