Skip to content

gc: 05_closure_capture retains +16.44% with every collector counter at +0.00% — surfaced when the ratchet started measuring again #7559

Description

@proggeramlug

05_closure_capture retains +16.44% with every collector counter unchanged

Surfaced the moment the GC ratchet started measuring again (#7557 — its probes had been skipped entirely since 2026-08-05 because a harness-validation step ran before them).

Of the 10 gating breaches that became visible, most are improvements wearing a two-sided band. This one is not. Reported by the agent that restored the ratchet, on two machine classes (the pinned quiet mini and a MacBook), with retention and counters reproducing bit-for-bit across hosts:

probe retention collector counters
05_closure_capture +16.44% +0.00% on every one
02_survivor_promotion +2.77%

Same minor_cycles, copied_objects, copied_bytes, promoted_objects, promoted_bytes, freed_bytesidentical work, more retained. That combination is the interesting one: it rules out "the collector ran differently" and points at something being kept alive that previously was not. A closure-capture probe holding 16% more is exactly the shape of an over-broad root or a capture cell that stopped being released.

Why it was invisible

The ratchet did not run between 2026-08-05 and #7557. In that window roughly fifteen GC-affecting changes landed: #7532 (typed-shape layout at allocation), #7535 (shape-install memo), #7536 (write-barrier gating), #7546 (spill-path value rooting), #7553 (tape out of the old generation), #7550/#7552 (for-init type propagation), plus the rooting stack #7495/#7516/#7527/#7529.

Several of those add rooting. #7516 alone rooted 14 sites, #7527 more, #7529 three more — and a root that is broader or longer-lived than necessary retains. That is the first place I would look, and it is worth saying plainly: these fixes were correct and must not be reverted (they closed real use-after-frees), but "more correct rooting" and "more retention" are the same lever pulled in opposite directions.

Suggested method

Bisect the probe's retention across that window with the ratchet itself now that it works — gc_ratchet.py measure --probe 05_closure_capture at each candidate commit on the pinned host. The counters being flat means a PERRY_GC_DIAG trace diff between the endpoints should show the difference as a retained-object delta rather than a cycle-behaviour one.

Note the measurement caveat from #7558: every probe reads process.memoryUsage() after an explicit gc(), which forces a conservative stack scan that systematically over-retains (measured at 8.28 MB / 16% on 12_large_live_set). Run the comparison with PERRY_CONSERVATIVE_STACK_SCAN=off as well, so a false-root artifact is not mistaken for a real regression — or confirmed as one.

Acceptance

Either the +16.44% is attributed to a specific commit and fixed, or it is shown to be a measurement artifact of the conservative scan and the probe's methodology is corrected. Do not re-pin it away without an answer — that is the decision the ratchet exists to force.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions