FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Static generation worker retains memory per prerendered page on 16.3.x — large sites OOM at default heap (regression from 16.2.12) · Issue #97464 · vercel/next.js · GitHub

/ next.js Public

Static generation worker retains memory per prerendered page on 16.3.x — large sites OOM at default heap (regression from 16.2.12) #97464

Description

Link to the code that reproduces this issue

https://github.com/skurekjakub/next-16-3-static-gen-oom-repro

To Reproduce

The repro is a dependency-free App Router app: a catch-all route reads a markdown file with readFileSync, splits it into paragraphs and renders them, with one connection()-gated component under Suspense so every page is a partial prerender. cacheComponents: true, Turbopack. experimental: { cpus: 1 } pins the build to one static-generation worker — which is what a 2-core CI agent gets by default. The content loader logs worker heap usage every 100 pages.

  1. npm install && npm run gen (generates 2,500 markdown files, ~200 KB each)

  2. NODE_OPTIONS=--max-heap-size=2048 npm run build — simulates the worker's default heap on a 7 GB CI machine (--max-old-space-size is deliberately not used; see Additional context)

  3. On main (next 16.3.1) the worker heap grows ~0.9 MB per prerendered page and the build OOMs at ~72%:

    [heap] pages=100  heapUsedMB=221  rssMB=563
    [heap] pages=700  heapUsedMB=779  rssMB=1976
    [heap] pages=1300 heapUsedMB=1391 rssMB=3367
    [heap] pages=1800 heapUsedMB=1814 rssMB=4211
    FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
    ⨯ Next.js build worker exited with code: null and signal: SIGABRT
    
  4. git checkout next-16.2.12 && npm install and run the identical build (next 16.2.12) — worker heap stays flat and the build completes:

    [heap] pages=100  heapUsedMB=114  rssMB=386
    [heap] pages=1000 heapUsedMB=123  rssMB=470
    [heap] pages=1900 heapUsedMB=157  rssMB=487
    [heap] pages=2500 heapUsedMB=109  rssMB=484
    ✓ Generating static pages using 1 worker (2504/2504) in 65s
    

Retention scales with rendered page size (~0.3 MB retained per 30 KB page, ~0.9 MB per 200 KB page — SIZE_KB knob in the repro), so smaller sites just hit the wall later.

Current vs. Expected behavior

Current: on 16.3.x the static-generation worker retains memory for every prerendered page, so the reachable page count is bounded by the worker's heap. On 16.2.12 heap usage is flat and effectively unbounded page counts fit in the default ~2 GB. First observed in production CI: a real ~2,300-page documentation site that built fine for months on 16.2.12 (Azure DevOps hosted agents, 2 vCPU / 7 GB) started OOMing at ~70% immediately after upgrading to 16.3.1, with identical content.

Partial contributor identified: enablePrerenderSourceMaps became default-true in 16.3 and adds --enable-source-maps to the worker (Node keeps its source-map cache in the JS heap). Disabling it only delays the OOM by ~10–15% of pages — the per-page growth remains, so most of the regression is elsewhere in 16.3 static generation.

Expected: worker heap usage roughly flat across pages, as on 16.2.12.

Provide environment information

Operating System:
  Platform: linux (WSL2; also reproduced on Azure DevOps hosted ubuntu-24.04 agents, 2 vCPU / 7 GB RAM)
  Arch: x64
  Available memory (MB): 31931
  Available CPU cores: 20
Binaries:
  Node: 24.18.0 (CI: 24.19.0)
  npm: 12.0.1
Relevant Packages:
  next: 16.3.1 // Latest available version is detected (16.3.1).
  react: 19.2.7
  react-dom: 19.2.7
Next.js Config:
  cacheComponents: true

Which area(s) are affected? (Select all that apply)

Performance, Turbopack, Cache Components

Which stage(s) are affected? (Select all that apply)

next build (local), Other (CI)

Additional context

Low priority for us since we have a workaround — filing for visibility, because anyone with a few thousand static pages on small CI machines will hit this on upgrade.

Two things that made this harder to diagnose than necessary:

  • The documented mitigation (NODE_OPTIONS=--max-old-space-size=...) silently does nothing for this failure: since Run export child process with runtime's default max-old-space-size #78712, createStaticWorker passes isolatedMemory: true and lib/worker.js deletes max-old-space-size/max_old_space_size from the worker's NODE_OPTIONS, so the cap only ever reaches the parent process. A note in the memory-usage guide would save people a round of "the fix didn't work".
  • Workaround we ship today: NODE_OPTIONS=--max-heap-size=4096 — this V8 flag is not on the strip list and propagates into the worker environment (verified via /proc/<worker>/environ). It works, but only by accident of the blocklist.

#95751 (experimental.staticWorkerMaxOldSpaceSize) looks like the intended knob for the heap side — happy to test it. The per-page retention itself, though, looks like the actual bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Cache ComponentsRelated to the `cacheComponents`, `useCache`, or `ppr` experimental flags.PerformanceAnything with regards to Next.js performance.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


    Back | FazBrowse Home | New Git URL