Downstream: zig-utils/zig-js#97 Related: zig-utils/zig-js#62, zig-utils/zig-js#101, #4
Evidence
Current exact direct and 8-lane shared object_churn profiles both retain Heap.sweepPhaseLocked / the embedder backing allocator free path as a major nursery cost after allocation publication and tail trimming were optimized. Direct sampling attributed 803 samples to sweep and 537 to the zig-js cell-backing free function; the shared coordinator attributed 474/727 young-collection samples to sweep and 147 directly to backing free. Each dead cell currently crosses std.mem.Allocator.free independently.
Design
- buffer a small fixed number of already-finalized/unindexed/unlinked dead cell allocations during sweep;
- expose an optional binding batch-free hook, with the existing individual allocator free loop as the generic byte-for-byte fallback;
- flush without collector allocation or new OOM behavior;
- let slab embedders amortize size-class locking and dispatch while preserving individual size/chunk accounting.
Acceptance criteria
- Finalize, unindex, clear magic, unlink, and decrement counters before an allocation is eligible for reuse.
- Preserve mixed kinds/sizes, minor/full collection, generic allocator fallback, and teardown behavior.
- Use fixed collector scratch; introduce no sweep-time allocation or OOM path.
- Add generic fallback and optional-hook tests.
- Pass zig-gc units and TSan, plus downstream zig-js focused/full/no-GIL gates.
- Exact-parent object-churn A/B keeps checksums and materially improves throughput without regressing direct mode.