JavaScript heap out of memory
What triggers V8's JavaScript heap out of memory crash, how to raise the heap limit for a one-off large task, and when it signals a real leak.
Last updated
<--- Last few GCs ---> FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory 1: 0xb01610 node::Abort() [node]
What it means: V8, the engine Node runs on, hit its memory ceiling and can't allocate more, so the process aborts rather than continue against an exhausted heap.
Likely causes
Most probable first — with how to confirm.
- 1.The task genuinely needs more memory than V8's default heap limit allows — large builds, big JSON parses, or bundling many files at once are common triggers; the exact default varies by Node version and available system memory.
- 2.A memory leak keeps objects referenced longer than they should be — unclosed listeners, growing caches, or closures holding onto large data — a heap snapshot via `--inspect` and Chrome DevTools will show what's accumulating.
- 3.You're loading a large dataset entirely into memory at once (`readFileSync` on a huge file, building one giant array) instead of streaming it.
Fixes
Safest first; destructive ones are called out.
- →Raise the heap limit for a one-off large task: `node --max-old-space-size=4096 script.js` (value in MB) — the standard first fix, and safe since it only affects that process.
- →For npm scripts, set it so the flag reaches the actual process: `NODE_OPTIONS=--max-old-space-size=4096 npm run build` (macOS/Linux), `set NODE_OPTIONS=--max-old-space-size=4096 && npm run build` (cmd.exe), or `$env:NODE_OPTIONS='--max-old-space-size=4096'; npm run build` (PowerShell — `set` and `&&` both fail there).
- →If it recurs at the same input size, profile for a leak rather than keep raising the limit — pushing the ceiling higher only postpones the same failure.
- →Switch to streaming APIs (`fs.createReadStream`, a streaming JSON parser) for large files instead of loading everything into memory at once.
Part by part
- Last few GCs
- V8 printing its final garbage-collection attempts before giving up. The timings there show collection taking longer and reclaiming less, which is the shape of a heap about to fail.
Common mistakes
node --max-old-space-size=8192 script.jsRaising the ceiling is the right move for a genuinely large workload and the wrong one for a leak, where it buys time and fails later with more memory involved. Decide which you have before reaching for the flag.
Adding more RAM to the machineV8 has its own heap limit independent of system memory, so more RAM changes nothing without also raising --max-old-space-size. This is a common and expensive misdiagnosis on cloud builds.
Restarting the process on a scheduleA legitimate mitigation for a leak you cannot fix today, and it should be recorded as one rather than treated as a solution — the underlying growth is still there and will eventually outrun the interval.
Questions
What is the default heap limit?
It depends on the Node version and the machine, but is typically around 2GB on 64-bit systems and can be lower in containers. Node 12 and later attempt to read a container memory limit, which older versions ignored entirely.
How do I tell a leak from a large workload?
Watch memory across several runs or a long-running process. A workload peaks and returns; a leak trends upward and never comes back down. That distinction decides whether raising the limit is a fix or a delay.
How do I find what is holding memory?
Take a heap snapshot with --heapsnapshot-signal or through the inspector, then compare two taken minutes apart. The objects that grew between them are where to look — usually a cache, a listener, or an array nobody clears.
Why does this happen during a build rather than at runtime?
Bundlers hold the whole module graph in memory, so build memory scales with project size rather than with traffic. Limiting parallelism often helps more than raising the heap, since each worker is a separate process with its own limit.