You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Old-generation page defragmentation is implemented and unit-testable, but production selection is off by default behind PERRY_GC_OLD_DEFRAG=1. It was disabled after #6206 reproduced a stale non-heap reference to a moved old object (6/6 corruption with defrag enabled).
That was the correct containment action, but #6206 is closed and there is no open successor tracking restoration of the rewrite contract. The default collector therefore cannot compact partially-live old pages.
Current consequence
The only default reclamation inside a partially-live old block is the old free list, which is exact-size only. That is effective for uniform promoted cohorts, but mixed-size churn leaves holes that cannot satisfy future differently-sized allocations; a block with any live object remains mapped.
This leaves a structural RSS weakness for long-lived servers with heterogeneous promoted objects: logical heapUsed can fall while old blocks stay mapped due to scattered live objects and unusable size buckets.
Required work
Treat this as a rewrite-contract project, not simply an env-default flip:
Enumerate every metadata/IC/cache path that can retain an old movable address and require either registered rewrite coverage, epoch invalidation, pinning, or a written immovability proof.
Re-enable defrag only after the reproducer and a dependency-scale stress corpus are clean.
Add a mixed-size old-gen fragmentation ratchet that distinguishes live bytes, exact-fit free bytes, mapped old-block bytes, and bytes actually released.
PARSE_KEY_RING in scripts/gc_runtime_root_holders.json is already classified as an open_gap whose safety currently depends specifically on old-gen defrag being off. That exemption must be resolved before the default can flip.
No unrewritten old movable addresses remain in runtime caches/ICs/side tables; the inventory encodes and gates the contract.
Defrag can be enabled by default under GC stress without corruption.
A mixed-size retention probe demonstrates mapped old-gen pages converge toward live bytes after a full reclaim, within a pinned allowance.
Until then, user-facing/current architecture docs state explicitly that old-page compaction is disabled and exact-fit hole reuse is the only partial-block mitigation.
Summary
Old-generation page defragmentation is implemented and unit-testable, but production selection is off by default behind
PERRY_GC_OLD_DEFRAG=1. It was disabled after #6206 reproduced a stale non-heap reference to a moved old object (6/6 corruption with defrag enabled).That was the correct containment action, but #6206 is closed and there is no open successor tracking restoration of the rewrite contract. The default collector therefore cannot compact partially-live old pages.
Current consequence
The only default reclamation inside a partially-live old block is the old free list, which is exact-size only. That is effective for uniform promoted cohorts, but mixed-size churn leaves holes that cannot satisfy future differently-sized allocations; a block with any live object remains mapped.
This leaves a structural RSS weakness for long-lived servers with heterogeneous promoted objects: logical
heapUsedcan fall while old blocks stay mapped due to scattered live objects and unusable size buckets.Required work
Treat this as a rewrite-contract project, not simply an env-default flip:
PARSE_KEY_RINGinscripts/gc_runtime_root_holders.jsonis already classified as anopen_gapwhose safety currently depends specifically on old-gen defrag being off. That exemption must be resolved before the default can flip.Acceptance criteria