mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-24 06:14:01 +08:00
Full passes are scheduled by heap growth, and the fraction they wait for widens while growth-triggered passes find nothing, up to 16 times the heap after the last full pass. That keeps a large live heap that keeps growing (a compiler checking a large program) from being walked again and again. But growth alone cannot tell such a heap from one that keeps garbage: a program that drops a large structure and builds another, as a compiler rebuilding in watch mode does, holds the dead structures' cyclic parts until the heap has grown to 17 times its size. Ten rebuilds of benchmarks/regions/compiler.ts without lifetime regions reached 1.3 GB that way. The acyclic parts of such a structure go by reference counting, and that shows: the live count falls between passes. The collector now sums those falls since the last full pass. Once they reach 1/32 of the heap that pass left and have stopped (a structure torn down by reference counting falls over several checks, and a pass in the middle would find its rest still referenced), a full pass is due as soon as the heap has grown the base quarter past it, whatever the growth fraction. One that finds fewer older objects than had died doubles the fall the next one needs, up to 256 times, and a productive one restores it. A full pass that the growth rule did not choose (this one, a backlog pass, a scheduled-age pass) now raises the growth baselines of the levels it walked but does not lower them. After such a pass reclaims a dropped structure, the heap regrowing to where it was is the program rebuilding what it had. From the low point, growth passes at both levels would walk the rebuilt live heap and find nothing. On the ten rebuilds that was a third of the collector's added time. A heap that only grows never falls, so its schedule is unchanged. The checks run where the release path and the event loop already decide which pass to run.