Skip to content

memory(gc): copied minor subtracts from-space high-water from an exact live census #7901

Description

@proggeramlug

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

  • Track or derive live from-space bytes, distinct from block high-water, when converting an exact aggregate census through a copied minor.
  • Preserve exact non-from-space live bytes across copied minors, including prior promotions, long-lived objects, old holes, and partially-live Eden blocks.
  • Add a regression sequence: seed an old live cohort; run a non-moving/full sweep leaving one young survivor in a partially dead block; run a copied minor; assert heapUsed == old_live + copied/promoted_live byte-for-byte.
  • Add an internal checked/debug invariant that prevents a high-water subtraction from silently consuming unrelated generations.
  • Keep high-water/reserved telemetry separate for fragmentation analysis.

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    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