Summary
The new exact live-byte census can undercount after collector modes alternate. A full/non-moving sweep publishes exact aggregate live bytes, excluding dead objects left as holes in partially-live Eden blocks. The next copied minor derives its census by subtracting copying_from_space_in_use_bytes(), which is the sum of block bump offsets and therefore includes those already-excluded dead holes.
That subtracts the same dead space a second time. saturating_sub can hide the arithmetic failure and, when the young high-water exceeds unrelated old live bytes, erase old-generation occupancy from heapUsed and major-GC pacing altogether.
Concrete accounting
After a non-moving sweep, suppose:
- old/long-lived live bytes = 40 MiB
- Eden high-water = 10 MiB
- actual live Eden objects = 1 MiB (9 MiB dead holes in partially-live blocks)
- published exact census = 41 MiB
On the next copied minor, if the 1 MiB survives, current code computes:
41 MiB - 10 MiB from-space high-water + 1 MiB survivors = 32 MiB
The correct result is still 41 MiB. With less than 9 MiB of unrelated live data, saturating subtraction reduces the non-from-space component to zero.
Evidence
Impact
process.memoryUsage().heapUsed and V8-compatible heap telemetry can underreport live memory.
- Major/full-GC pacing can be delayed because it consumes the same undercounted metric.
- The error persists in the next census and can grow across mode transitions until a later whole-heap object census repairs it.
- The use of saturating arithmetic makes the invariant violation silent.
Acceptance criteria
Audit context
Found during the v0.5.1481 re-audit of #7879/#7886. The original high-water-as-live defect is fixed for direct post-collection reads; this issue is a cross-mode derivation error in the replacement census.
Summary
The new exact live-byte census can undercount after collector modes alternate. A full/non-moving sweep publishes exact aggregate live bytes, excluding dead objects left as holes in partially-live Eden blocks. The next copied minor derives its census by subtracting
copying_from_space_in_use_bytes(), which is the sum of block bump offsets and therefore includes those already-excluded dead holes.That subtracts the same dead space a second time.
saturating_subcan hide the arithmetic failure and, when the young high-water exceeds unrelated old live bytes, erase old-generation occupancy fromheapUsedand major-GC pacing altogether.Concrete accounting
After a non-moving sweep, suppose:
On the next copied minor, if the 1 MiB survives, current code computes:
The correct result is still 41 MiB. With less than 9 MiB of unrelated live data, saturating subtraction reduces the non-from-space component to zero.
Evidence
copying_from_space_in_use_bytes()sums Eden and active-survivor block offsets; it is a high-water/placement quantity, not live occupancy.sweep.arena_live_bytes, the object-walk census.arena_live_allocated_bytes()explicitly describes the stored value as an exact post-GC census.Impact
process.memoryUsage().heapUsedand V8-compatible heap telemetry can underreport live memory.Acceptance criteria
heapUsed == old_live + copied/promoted_livebyte-for-byte.Audit context
Found during the v0.5.1481 re-audit of #7879/#7886. The original high-water-as-live defect is fixed for direct post-collection reads; this issue is a cross-mode derivation error in the replacement census.