--- id: wiki-2026-0508-nodejs-memory-tuning title: Node.js Memory Tuning category: 10_Wiki/Topics status: verified canonical_id: self aliases: [V8 Heap Tuning, Node Heap Limit, max-old-space-size] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [nodejs, v8, performance, memory, gc] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: JavaScript framework: Node.js 22 LTS --- # Node.js Memory Tuning ## 매 한 줄 > **"매 V8 heap 의 explicit budget + GC trace 의 observe"**. Node.js 의 default heap 매 ~4 GB (64-bit) 의 OS-derived — 매 process 의 actual workload 의 맞춰 매 `--max-old-space-size`, `--max-semi-space-size` 의 tune. 2026 Node 22 LTS 의 V8 12.4 — 매 Maglev tier, pointer compression default 의 활용. ## 매 핵심 ### 매 V8 heap layout - **Young generation (semi-space)**: 매 short-lived — 매 Scavenge GC, 매 fast. - **Old generation**: 매 promoted — 매 Mark-Compact. - **Code/Map/Large object space**: 매 separate. - **Pointer compression**: 매 4-byte tagged pointer (4 GB heap 의 limit) — 매 default since Node 14. ### 매 GC modes - **Scavenge** (minor): 매 ms order, 매 frequent. - **Mark-Sweep-Compact** (major): 매 100s of ms, 매 STW phase. - **Concurrent marking**: 매 background — STW 의 reduce. - **Incremental marking**: 매 chunk-by-chunk. ### 매 응용 1. 큰 JSON parsing — heap 의 raise + streaming parser 의 use. 2. Long-running server — leak detection (heap snapshot diff). 3. Worker thread — per-thread heap budget. 4. Container env (k8s) — request/limit 와 V8 의 align. ## 💻 패턴 ### CLI flags (Node 22) ```bash NODE_OPTIONS="--max-old-space-size=8192 \ --max-semi-space-size=128 \ --expose-gc \ --trace-gc \ --trace-gc-verbose" \ node server.js ``` ### Container-aware (k8s) ```yaml env: - name: NODE_OPTIONS # 매 limit 의 ~80% 의 leave headroom for native + libuv value: "--max-old-space-size=3200" resources: limits: memory: "4Gi" ``` ### Programmatic — heap stats ```js import v8 from 'node:v8'; import { performance } from 'node:perf_hooks'; setInterval(() => { const s = v8.getHeapStatistics(); console.log({ used_mb: (s.used_heap_size / 1e6).toFixed(1), total_mb: (s.total_heap_size / 1e6).toFixed(1), limit_mb: (s.heap_size_limit / 1e6).toFixed(1), external_mb: (s.external_memory / 1e6).toFixed(1), }); }, 5000); ``` ### Heap snapshot on threshold ```js import v8 from 'node:v8'; import fs from 'node:fs'; function snapshotIfHigh(thresholdRatio = 0.85) { const s = v8.getHeapStatistics(); if (s.used_heap_size / s.heap_size_limit > thresholdRatio) { const path = `heap-${Date.now()}.heapsnapshot`; v8.writeHeapSnapshot(path); console.error('Heap snapshot:', path); } } setInterval(snapshotIfHigh, 60_000); ``` ### GC observer (perf_hooks) ```js import { PerformanceObserver, constants } from 'node:perf_hooks'; new PerformanceObserver((list) => { for (const e of list.getEntries()) { const kind = e.detail?.kind; if (e.duration > 50) { console.warn('Long GC', { kind, ms: e.duration.toFixed(1) }); } } }).observe({ entryTypes: ['gc'], buffered: false }); ``` ### Streaming JSON (avoid heap blowup) ```js import { parser } from 'stream-json'; import { streamArray } from 'stream-json/streamers/StreamArray.js'; import fs from 'node:fs'; fs.createReadStream('big.json') .pipe(parser()) .pipe(streamArray()) .on('data', ({ value }) => process(value)); ``` ### Buffer pool tuning ```js // 매 large Buffer.allocUnsafe 의 pool — > 8KB 매 bypass import { Buffer } from 'node:buffer'; Buffer.poolSize = 64 * 1024; // default 8 KB ``` ### `--heap-prof` for sampling ```bash node --heap-prof --heap-prof-interval=524288 server.js # → Chrome DevTools 의 .heapprofile 의 load ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Container limit 4 GB | `--max-old-space-size=3200` (~80%) | | OOM at startup | Streaming parse + lazy load | | Long GC pauses | Reduce old-gen pressure (pool, reuse) + bigger semi-space | | Memory leak suspect | Heap snapshot diff (3 snapshots, 30s apart) | | Worker threads | Per-worker heap budget — sum < container limit | | > 4 GB heap | `--no-pointer-compression` 의 disable (slower, more RAM) | **기본값**: `--max-old-space-size = 0.8 × container_limit`, GC trace in prod 의 sample. ## 🔗 Graph - 부모: [[V8]] - Adjacent: [[Pointer Compression]] ## 🤖 LLM 활용 **언제**: flag lookup, OOM diagnosis playbook, snapshot 의 interpret. **언제 X**: 매 specific leak — heap snapshot 의 actual data 의 require. ## ❌ 안티패턴 - **Default heap 의 trust in container**: 매 OOMKilled — explicit `--max-old-space-size`. - **`global.gc()` polling**: 매 mask 의 leak — root cause 의 fix. - **Heap limit = container limit**: 매 native + libuv overhead 무시 — 80% 의 leave. - **Synchronous huge JSON.parse**: 매 STW — streaming parser 의 use. - **Heap snapshot in hot path**: 매 STW seconds — threshold trigger 의 만. ## 🧪 검증 / 중복 - Verified (nodejs.org docs v22; v8.dev blog; Node Diagnostics Working Group). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — V8 heap tuning + GC trace + snapshot 정리 |