Skip to content

gc: restore a safe old-generation defragmentation rewrite contract; production compaction is disabled #7876

Description

@proggeramlug

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 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:

  1. Reproduce and localize GC evacuation leaves a stale reference to a moved array element (wild-pointer crash / corrupt cached value) #6206's stale non-heap address with from-space/old-page protection.
  2. 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.
  3. Re-enable defrag only after the reproducer and a dependency-scale stress corpus are clean.
  4. 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.

Acceptance criteria

  • GC evacuation leaves a stale reference to a moved array element (wild-pointer crash / corrupt cached value) #6206's reproducer is deterministic red with the unsafe baseline and green with defrag enabled after the fix.
  • 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.

Metadata

Metadata

Assignees

Labels

bugConfirmed defect or regressionperformanceRuntime, compile-time, build-size, or memory performance

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions