LSP Memory Audit
Stage 6 / v1.0 RC readiness review of LSP heap behaviour. Complements the latency + idle-heap benchmarks in Performance Benchmarks.
Audit date: 2026-06-24
Harness: scripts/audit-lsp-memory.mjs
Committed report: benchmarks/lsp-memory-audit.json
Reproduce:
cd superjs && pnpm nx build lsp
cd .. && node --expose-gc scripts/audit-lsp-memory.mjs
# optional heap snapshot for Chrome DevTools:
node --expose-gc scripts/audit-lsp-memory.mjs --snapshotSummary
| Check | Result | Target |
|---|---|---|
| Idle heap (~100k LOC corpus, single file) | 195.8 MB | ≤ 250 MB |
didClose releases analysis state | 186.8 MB freed (ratio 1.0) | ≥ 35% release |
| LRU evicts oldest file under 2 MiB budget | oldest file empty on query | — |
| Multi-file plateau (5 × ~20k LOC) | 45 MB growth | ≤ 120 MB |
Verdict: No unbounded retention found. Per-file state is released on close and LRU eviction; dominant cost is the in-memory AST + type environment for the active file(s).
What is retained per open document
- Source text —
LspServer.sourcesandCompiler.rawSourcesshare the same string reference fromtextDocument/didOpen(not triple-allocated). FileState(compiler) — parsed AST, diagnostics, typed spans, lowered IR snapshot, and module export surface. This is the bulk of heap usage (~190 MB for ~140k LOC synthetic corpus vs ~5.3 MB source text).- Touch metadata — URI → LRU tick map (negligible).
Eviction path (enforceBudget or didClose) calls compiler.removeFile, which
drops files and rawSources entries for that URI.
Known limitations (post-1.0)
| Item | Status |
|---|---|
lsp.memoryBudgetMB estimates bytes from source length only | Does not account for AST/type overhead; a single huge file can exceed true heap budget while under byte estimate |
No SJS-W010 client diagnostic on heap pressure | Spec'd; not yet emitted (README notes as later work) |
| JSON-RPC 8 MiB message cap | Planned (threat model T3) |
| Per-phase memory breakdown | Not instrumented in compiler pipeline |
Relation to CI
- Bench gate:
scripts/check-bench-results.mjsenforces idle heap + P99 hover frombenchmarks/results.json. - Audit gate:
scripts/check-lsp-memory-audit.mjsenforces close + LRU checks frombenchmarks/lsp-memory-audit.json.
Last updated: 2026-06-24.