Node.js で「JavaScript heap out of memory」が発生する
V8 のヒープ上限(既定 1.5〜4GB 程度)を超えるとプロセスが落ちる。
--max-old-space-size でヒープを増やすか、ストリーム化 / リーク修正で使用量を減らす。
CI ビルドや巨大 JSON の処理で頻発。
公開:
要約
FATAL ERROR: ... JavaScript heap out of memory は V8 の old space が上限に達した とき出る。
短期的には --max-old-space-size で上限を引き上げ、根本対処として ストリーム化 と リークの特定 を行う。
CI でだけ発生する場合は実行マシンの空きメモリ自体も疑う。
よくある原因
- 上限不足: ビルドや巨大 JSON の処理で、V8 既定のヒープ上限を超える
- 一括展開:
fs.readFileSyncで巨大ファイル全体を Buffer に乗せている、JSON.parseで 100MB の配列を一気にパースしている - リーク: モジュールスコープの Map に push しっぱなし、
removeListenerし忘れ、setIntervalをclearIntervalしていない - ビルドツールのピーク: webpack / vite / tsc の型チェック・ソースマップ生成のピークがマシンを超える
解決策
1. ヒープ上限を上げる
# 一回限り
node --max-old-space-size=4096 ./script.js
# 環境変数で常時
export NODE_OPTIONS="--max-old-space-size=4096"
npm run build公式 CLI ドキュメント の通り単位は MB。
物理メモリの 70〜80% を上限の目安にする。
2. ストリーム化する
import fs from "node:fs";
import readline from "node:readline";
const rl = readline.createInterface({
input: fs.createReadStream("./huge.jsonl"),
crlfDelay: Infinity,
});
for await (const line of rl) {
const row = JSON.parse(line);
process(row);
}JSON Lines や CSV はストリームと相性が良く、メモリ消費は定数化する。
3. ヒープスナップショットでリーク特定
node --inspect ./server.js
# Chrome DevTools → Memory → Take heap snapshotHeap Snapshot 公式ガイド の手順で 2 回スナップショットを取り、Comparison で増え続けているオブジェクトを見つける。
リスナや Map の参照を疑う。
4. ビルドツール側で抑える
- webpack:
cache: { type: "filesystem" }、devtool: falseで sourcemap を消す - vite:
build.sourcemap: false、build.rollupOptions.output.manualChunksで分割 - tsc:
--incremental+--buildで差分化、skipLibCheck: trueで型チェック節約
CI なら NODE_OPTIONS=--max-old-space-size=4096 を env に入れるのが手っ取り早い。
実行例
実際に上記の手順を node:20 環境で動かすと、ヒープ上限を 32MB に絞った状態では FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory が発生して終了コード 134 で強制終了し、--max-old-space-size=512 を指定した再実行および NODE_OPTIONS 経由どちらのケースでも 30 ブロックの確保が成功して終了コード 0 となった。
== versions ==
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
v20.20.2
Python 3.11.2
git version 2.39.5
This is not the tsc command you are looking for
To get access to the TypeScript compiler, tsc, from the command line either:
- Use npm install typescript to first add TypeScript to your project before using npx
- Use yarn to avoid accidentally running code from un-installed packages
v10.5.0
== run ==
--- Node.js バージョン ---
v20.20.2
--- ヒープ上限 32MB で実行(FATAL ERROR ... JavaScript heap out of memory を想定)---
<--- Last few GCs --->
[62:0x4523dbc0] 28 ms: Scavenge 25.9 (30.4) -> 25.9 (34.4) MB, 1.08 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[62:0x4523dbc0] 35 ms: Mark-Compact 33.5 (42.0) -> 33.0 (49.5) MB, 4.24 / 0.00 ms (+ 0.1 ms in 2 steps since start of marking, biggest step 0.1 ms, walltime since start of marking 12 ms) (average mu = 1.000, current mu = 1.000) finalize incremental
<--- JS stacktrace --->
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----
1: 0xb78db3 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [node]
2: 0xee8300 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]
3: 0xee85e7 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]
4: 0x10fb205 [node]
5: 0x1113088 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
6: 0x1114ffa v8::internal::Heap::HandleGCRequest() [node]
7: 0x1080237 v8::internal::StackGuard::HandleInterrupts() [node]
8: 0x1523dda v8::internal::Runtime_StackGuard(int, unsigned long*, v8::internal::Isolate*) [node]
9: 0x195e476 [node]
Aborted (core dumped)
終了コード: 134
--- 解決: --max-old-space-size=512 にヒープ上限を広げて再実行 ---
全 block の確保に成功: 30 blocks
終了コード: 0
--- 別解: NODE_OPTIONS 経由でも同様に上限を渡せる ---
全 block の確保に成功: 30 blocks
終了コード: 0— 2026-06-17 時点の出力