Heap growth in TypeScript Builder during incremental compilation with project references
0 reputation · 02 Jan 2022, 09:31 UTC
0 reputation · 02 Jan 2022, 09:31 UTC
In TypeScript environments utilizing --incremental and --composite flags, the Builder component manages the lifecycle of SourceFile objects to optimize rebuild speeds. While TypeScript 4.5 introduced mechanisms to dispose of outdated nodes during the clean phase, specific behaviors persist in complex dependency graphs.
When a composite project references another composite project, there is uncertainty regarding how the language service handles transitive dependency changes. If the Builder prioritizes compilation speed over full correctness, stale type information may remain in the IDE despite the memory leak mitigations.
Given a multi-project setup running in watch mode, how does the compiler ensure that transitive changes trigger the necessary cache invalidation across all upstream projects? Does the current reference counting mechanism in the Program object fully prevent heap growth when deep dependency chains are modified frequently?
34775 reputation · 02 Jan 2022, 15:24 UTC
Heap growth during incremental compilation in composite projects is typically caused by the TypeScript compiler retaining SourceFile and Program objects in the Builder or language-service internal caches. While TypeScript 4.5+ improved disposal of stale nodes, deep dependency chains in watch mode can prevent the garbage collector from reclaiming memory if transitive references are not explicitly invalidated or disposed of.
The Builder uses a reference-counting mechanism to manage dependencies. In a composite project setup, when one project references another, the compiler relies on .tsbuildinfo files. If a transitive dependency changes frequently, the language service may keep older versions of the project graph in memory to ensure type-checking consistency during the rebuild cycle. If the dependency graph is complex, the Program object may not be de-referenced quickly enough for the heap to be cleared, leading to the observed memory growth.
--incremental and --composite flags enable the use of .tsbuildinfo to track file hashes and dependencies."composite": true properly set to allow the compiler to use declaration files for references.tsbuildinfo files; sometimes deleting the output directories can force a clean state that resets the reference counting logic.--max-old-space-size flag in Node to limit the heap, though this may impact performance if set too low.SourceFile objects, which prevents the GC from reclaiming them.To provide a more specific recommendation, are you using any custom TypeScript plugins or specific IDE extensions (like VS Code) when observing this memory growth?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.