Repository navigation
gc(macos): back Apple GC blocks with tagged 1 MiB regions - #12391
Merged
Merged
Conversation
added 3 commits
October 11, 2026 08:11
BufferHeader's fields became private to the buffer store, so the macOS-only fixture in length_handle_band_tests no longer compiled and the runtime test crate failed to build on macOS. Write the header's length, capacity and link words directly.
On Apple platforms arena blocks now come from their own anonymous mappings instead of mimalloc: aligned to the 1 MiB block size and tagged VM_MEMORY_APPLICATION_SPECIFIC_2 (241) so they show up apart from mimalloc (240) in vmmap and footprint tools. They stay in the existing region registry, so classification and pooling are unchanged. Idle pool and Eden pages are released with MADV_FREE_REUSABLE, which drops them from the physical footprint at once, and are reactivated with MADV_FREE_REUSE before reuse: on every pool handoff, and in Arena::set_current for Eden blocks. Every block becomes current before its bytes are written and idle discard never touches the current block, so the bump path carries no check. Blocks stay 1 MiB. 2 MiB blocks added 4 to 6 MiB of arena capacity on Apple with no huge-page benefit to pay for it. Linux and other targets are unchanged.
With Apple GC blocks in their own regions, block allocation no longer passes through mimalloc, and mimalloc runs its deferred purges on its own allocation traffic. Freed collector and runtime memory then stayed dirty until the next full collection: an Effect workload peaked 5 MiB higher than with mimalloc-backed blocks, which had been reusing that memory. GC counts and arena sizes were identical; MIMALLOC_PURGE_DELAY=0 removed the gap, and a forced mi_collect on a time gate removed it without the cost of purging on every free. When the GC heap maps a fresh region, run mi_collect(true) at most once per mimalloc's default purge delay (10 ms) on that thread. PERRY_GC_MALLOC_PURGE=0 disables it together with the existing cycle purge.
proggeramlug
pushed a commit
that referenced
this pull request
Oct 11, 2026
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing (10)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
proggeramlug
pushed a commit
that referenced
this pull request
Oct 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On Apple platforms, GC arena blocks now come from their own anonymous mappings instead of mimalloc. They are 1 MiB, aligned to 1 MiB, and tagged
VM_MEMORY_APPLICATION_SPECIFIC_2(241) sovmmap/footprint tools show them separately from mimalloc (240). Linux (R2 reservation) and the other targets are unchanged. The regions stay in the existing non-Linux region registry, so classification and pooling are unchanged.MADV_FREE_REUSABLE, which drops them from the physical footprint immediately. They are reactivated withMADV_FREE_REUSEbefore reuse:Arena::set_currentfor idle Eden blocks. Idle discard never touches the current block, and every block becomes current before its bytes are written, so the bump path carries no check.MIMALLOC_PURGE_DELAY=0removed the gap. The fix: when the heap maps a fresh region, runmi_collect(true)at most once per mimalloc's default purge delay (10 ms) per thread.PERRY_GC_MALLOC_PURGE=0disables it together with the existing cycle purge, so this adds no new knob.value/dynamic_object.rs.BufferHeader's fields became private, and the fixture no longer compiled, soperry-runtime's test crate did not build on macOS at all.This follows #12247, the codegen floor fix for
*-apple-macosx*triples. Without that fix, every inline guard missed for objects below 2 TiB, where these regions land.Measurements
The machine is an M1 Max on macOS 26.5, which is shared, so primary metrics are retired instructions and lifetime peak physical footprint from
proc_pid_rusage. Runs are n=5, interleaved, and each timed run is preceded by an untimed run of the same binary. All builds are default auto-optimized, and every run matched Node 26.5.1 output.Rebased branch vs
mainata760b5497a;main-ris the same binary repeated, as the noise floor:Bold marks a difference beyond the larger same-binary span.
Effect. The Effect workload does not link on current
mainin either arm: duplicate_perry_birth_class/_perry_birth_empty_arrayacross two modules, unrelated to this change. It was measured on the previous base (7a5584c12d):Before the purge drain, that comparison was +5.3 MiB.
RSS, a secondary metric, can be higher (buffer +3.7 MiB, Fastify +0.9 MiB) while footprint is lower. Reusable pages stay resident until the kernel needs them, but they are not charged to the process.
Tests
cargo test --release -p perry-runtime -- --test-threads=1on macOS:mainwith the fixture fix (needed for the test crate to build): 5,668 passed, 10 failed.The ten failures are the same set on both. The eleventh on the branch is
gc::tests::net_transport::n9_a_stale_resolve_leaves_the_new_plan_and_backlog_alone(WouldBlockon a test socket read). It is flaky onmaintoo: it failed 2 of 3 isolated runs onmainand 3 of 3 on the branch.New tests:
arena::region::apple_tests:arena::block::reuse_window_tests:set_currentboth reactivate).gc::cycle_malloc_trim::region_growth_purge_tests: the purge is gated to once per purge delay, and fresh region maps reach the gate.The pool-pressure fixture now seeds the pool with region backing instead of
std::alloc.No version bump.