Repository navigation
Updates to mimalloc v3.6.0 (upstream release of 2026-10-23, 223 commits after v3.5.3) and adapts mimalloc::Option to it.
[dependencies]
mimalloc = { git = "https://github.com/amsokol/mimalloc", tag = "v3.6.0" }Before you upgrade: what can affect your program
-
Breaking change in this crate: three
mimalloc::Optionvariants were renamed, following upstream. Update the names if you use them:v3.5.3+1 v3.6.0 MIMALLOC_<NAME>Option::GuardedPreciseOption::UnsafeGuardedPreciseunsafe_guarded_preciseOption::MinimalPurgeSizeOption::ArenaPurgeMinSizearena_purge_min_sizeOption::CollectMergesStatsOption::StatsMergeOnCollectstats_merge_on_collectOption::name()returns the new names. Of the old environment variables, onlyMIMALLOC_MINIMAL_PURGE_SIZEis still accepted (mimalloc keeps it as a legacy name);MIMALLOC_GUARDED_PRECISEandMIMALLOC_COLLECT_MERGES_STATSare silently ignored now, so rename them in your deployment.Optionis#[non_exhaustive], andOption::ALLgrew from 39 to 45 entries. -
Cross-thread page reclaim is now on by default (
page_reclaim_on_free: 0 → 1). A thread that frees a block into an abandoned page can now take that page over, even when the page came from another thread. This is upstream's change to improve reuse and speed in multi-threaded programs; it can also change how much memory a program with many short-lived or pooled threads holds.- Since 3.6.0 the option is only a default for new heaps: each heap copies it when it is created. The heap behind the Rust global allocator is created before
main, soOption::PageReclaimOnFree.set(..)frommaindoes not change it. To get the 3.5 behaviour, start the process withMIMALLOC_PAGE_RECLAIM_ON_FREE=0.
- Since 3.6.0 the option is only a default for new heaps: each heap copies it when it is created. The heap behind the Rust global allocator is created before
-
Large freed ranges are now returned to the OS immediately. A freed range of at least
arena_purge_immediate_size(new option, 32 MiB by default, in KiB) is purged as it is freed, whateverpurge_delay × arena_purge_multsays. A program that frees blocks of 32 MiB or more will see its RSS drop at once instead of after about 4 s, and may pay more page faults when it allocates such blocks again. A negativepurge_delaystill never purges.Option::ArenaPurgeImmediateSize.set(..)works frommain(read on every free).collect(force)is unchanged. -
Memory layout of small blocks changed. Upstream now gives blocks a per-page, hashed cache offset for the first block, to spread small allocations over the CPU cache sets (upstream issue #1121), and moves 64 KiB blocks into large pages. The goal is fewer cache conflicts; the side effect is that the usable layout and the RSS of a program can differ somewhat from 3.5.x. Measure with
process_info().current_rssif memory matters to you. -
Transparent huge pages (Linux). Upstream reworked how the huge page size is detected (it now reads
hpage_pmd_size). If the kernel's huge page size is over 8 MiB (for example arm64 with 64 KiB pages), mimalloc now treats the system as having no THP and marks its mappingsMADV_NOHUGEPAGE. On the usual 2 MiB x86-64 setup nothing changes:allow_thpstill defaults to 2,no_thpstill builds withMI_DEFAULT_ALLOW_THP=0, andMIMALLOC_ALLOW_THP=0still overrides it. -
A heap profiler is now compiled in and always available. Upstream built a profiler into mimalloc (
MI_PROFILE, on by default). Upstream says it adds overhead only once it is activated withMIMALLOC_PROFILE=<basename>, which writes pprof snapshots (I did not measure the cost of having it compiled in). Its settings are the newOption::Profile*variants (MIMALLOC_PROFILE_ALLOC_INTERVAL,_INUSE_INTERVAL,_TIME_INTERVAL,_SAMPLE_RATE); the profiler reads them once at process start, so set them with environment variables, not withOption::setfrommain. Upstream also keeps detailed statistics per size bin always on. Limitation of this crate: upstream's CMake build detectsexecinfo.h/libunwindand definesMI_HAS_EXECINFOH; this crate'sbuild.rsdoes not, so on Unix the profiler records samples without call stacks (its backtrace function is a stub returning 0 frames). On Windows it usesCaptureStackBackTrace. I checked on Linux thatMIMALLOC_PROFILEwrites snapshot files; they contain no stacks.
Changes in this crate
-
c_src/mimallocisv3.6.0.build.rsneeded no change:src/static.cstill includes the sources, which upstream moved intosrc/alloc,arena,heap,prim,profileandutil(sooptions.cis nowsrc/util/options.c). -
mimalloc::Optionfollowsmi_option_tof 3.6.0: the three renames above and six new variants:Variant MIMALLOC_<NAME>Default Notes ProfileAllocIntervalprofile_alloc_interval1 GiB bytes allocated between profiler snapshots; 0 disables ProfileInuseIntervalprofile_inuse_interval0 (off) snapshot when in-use bytes grew by this much ProfileTimeIntervalprofile_time_interval0 (off) seconds between snapshots ProfileSampleRateprofile_sample_rate512 KiB profiler sampling rate in bytes StatsMergeThresholdstats_merge_threshold512 KiB merge thread-heap statistics into the heap after this many bytes; 0 disables ArenaPurgeImmediateSizearena_purge_immediate_size32 MiB (in KiB) see point 3; get_size()returns bytesThe four
Profile*options only matter whenMIMALLOC_PROFILEis set, and are read once at process start (use the environment variables).StatsMergeThresholdandArenaPurgeImmediateSizeare read on every use. -
Documentation updated to the 3.6.0 behaviour:
PurgeDelay,ArenaPurgeMult,ArenaPurgeMinSizeandcollectnow mention the immediate purge of large ranges;PageReclaimOnFreestates the new default and thatsetfrommainhas no effect on the global allocator's heap;AllowThpnotes the over-8 MiB huge page case; the README tuning table has rows forArenaPurgeImmediateSizeandPageReclaimOnFree. -
Version:
3.6.0. The+1revision suffix ofv3.5.3+1is dropped, because the upstream version moved. The Rust API otherwise matches v3.5.3+1:MiMalloc,usable_size,process_info,collect, and thesecure,overrideandno_thpfeatures. -
cc:Cargo.tomlstill requirescc = "1.8"(the minimum, so consumers are not forced to a newercc);Cargo.locknow pinscc 1.10.0(was 1.9.0). -
process_info()andcollect()are unchanged. I compared the C implementations ofmi_process_infoandmi_collectwith 3.5.3; they are identical on Unix, macOS and WASI, and the Windows version differs only in a string literal. The table of sources in theProcessInforustdoc still holds. -
Tested on Linux x86-64 in debug and release, and with the
secure,overrideandno_thpfeatures (option names/discriminants againstmi_options_print_out, set/get round trip,process_info, and RSS going back down after freeing 400 MiB), pluscargo clippyandcargo doc. Not tested on Windows, macOS, 32-bit or other targets in this release.
What changed in mimalloc 3.5.3 → 3.6.0 (upstream)
Upstream summary (its readme): "(v3) add initial profiling hooks and default profiler, "always on" detailed statistics per size-bin, and always-on profiling. Initial rust bindings. Better support for large OS page sizes, support a quiescent state based reclamation interface (for CPython), better cache line aware allocation, fix heap visitor where some pages might be skipped, improve fork support, avoid over-allocation for naturally aligned allocations, fix stderr console output for XBox, fix huge allocation rollback, refactor the source and include files, immediately purge very large allocations, fix bcrypt dependency on windows, fix heap-destroy bug when a fresh heap was allocated at the same address (giving a segfault), page_reclaim_on_free is now on by default."
Full list: microsoft/mimalloc@v3.5.3...v3.6.0. The parts below are grouped by what they mean for a user. Items marked (C API) are in mimalloc.h but not wrapped by this crate; items marked (CMake) are upstream build options that this crate's build.rs does not use.
Behaviour and defaults
page_reclaim_on_freeis on by default (cross-thread reclaim on free); the option is now a per-heap creation default. See point 2 above.- Freed ranges of at least 32 MiB are purged immediately (
arena_purge_immediate_size); an explicitmi_arenas_purge()(C API) was added (upstream issue #1422). Purging is also allowed inarena_free(PR #1448). - Options renamed (
guarded_precise→unsafe_guarded_precise,minimal_purge_size→arena_purge_min_size,collect_merges_stats→stats_merge_on_collect), new options for the profiler,stats_merge_thresholdandarena_purge_immediate_size.guarded_sample_ratenow means "guard an object about every N requested bytes" (was "1 out of N allocations"); its default stays 0 and it only matters withMI_GUARDED, which this crate does not enable. - Small blocks get a cache offset based on a hash of the page; large allocations get a cache offset as well; 64 KiB blocks move into large pages (issues #1121; PR #1339).
page_full_retainadapts when cross-thread frees revive full pages. Stat merging between thread heaps and the heap was reworked (a regression from it was fixed).- No over-allocation for naturally aligned requests (PR #1408); the "naturally aligned" condition was relaxed and is checked in debug builds.
- On Windows, stderr output no longer attaches to the parent console.
Profiling and statistics
- Built-in pprof heap profiler (protobuf and text formats), activated by
MIMALLOC_PROFILE, with allocation/in-use/time interval snapshots, a custom allocation-hook API (mimalloc-profile.h), mapped-library output for Windows and macOS, libunwind support on riscv and Alpine, a concurrent profiler hashtable, and fixes for profile timestamps, recursive profiler invocation, allocation failures, new heaps not being sampled, and a sample-rate reset for no-sample allocations. See point 6 for what this crate's build includes. - Detailed per-size-bin statistics are always collected; the stats output uses a huge bin and was cleaned up.
- Custom allocation tracking hooks with full allocation metadata (CMake:
MI_TRACK=CUSTOM); the tracking functions moved frommimalloc.htomimalloc/track.h.
New features
- Fork support: threads must be quiescent (not inside mimalloc) at the fork.
test-forkwas added. - Quiescent-state-based reclamation (QSBR) primitives, written for CPython (
mimalloc/qsbr.h). - (C API)
mi_thread_id_of,mi_theap_delete,mi_theap_quiescent_abandon,mi_free_small_local/mi_free_small_local_nonnull,mi_heap_set_debug_fill_preserve,mi_arenas_purge; an arena wrapper; heap visitors now document that they assume no concurrent heap access. - Tests were added for
reallocarrgrowth, zero count and overflow, and for arenas, bitmaps, fork, pprof and QSBR. mi_malloc_sizeandmi_malloc_usable_sizeare defined withoutMI_MALLOC_OVERRIDE;mi_free_sizesize-mismatch checking for small allocations (issue #1423), and-DMI_OPT_FREE_SMALL=OFF(CMake) keepsmi_free_sizefrom usingmi_free_small.- Upstream also added its own Rust crate and split it into a
-syscrate (issues #1405, PR #1449). This repository does not use it.
Bug fixes
mi_heap_visit_blockscould skip pages; owned OS pages are now visited,page_count_osis maintained, andarea->usedis more precise during a heap walk.- Heap delete/destroy with OS-owned pages; a segfault in
mi_heap_destroywhen a fresh heap was allocated at the address of a destroyed one; the theap cache clears itsheapfield on destroy; blocks freed by other threads are collected before a page'susedcount is reset. - Huge-allocation rollback: the failing chunk is republished and the chunkmap bit is restored if bits were cleared temporarily.
- A data race on the thread-done pthread key at process exit.
- Forced exit on Windows where threads are killed while they may hold a lock (issue #1377).
- Assertion failures with pinned pages and guards, with an empty theap in
_mi_page_update_sample_countdown, and a spurious warning formi_free_sizein guarded builds; a missing page abandon for QSBR pages. - Linux huge pages: the interaction of PMD-sized and transparent huge pages (PR #1431) and
MADV_NOHUGEPAGEfor oversized huge pages; a build error withMI_NO_THPafter that change; a-Wsign-comparewarning when readinghpage_pmd_size. mi_free_block_mtatomics: a relaxed CAS was tried and then rolled back to a singlecas_weak_acq_rel; theselfpointer read inmi_freeis relaxed because a pointer handoff between threads already establishes happens-before; a false positive in TSAN.
Performance (code generation)
mi_freeandmi_mallocfast paths: restored and simplified several variants, fields loaded early, tail calls improved for MSVC, an x64 path removed, a compiler barrier restricted to arm64,mi_prefetchdefinitions, and better code for x64, arm32 and riscv. The layout ofmi_page_tchanged to avoidldp/stpon arm64 (issue #1433). AVX2 is only used on 64-bit. Upstream does not give a combined benchmark for 3.6.0, so measure your own workload.
Platforms and build
- Windows:
ProcessPrngon Windows 8+ and a statically linkedBCryptGenRandomon UWP/Xbox (issue #1426); fixed a load error on Windows 7; UWP compilation; no morebcrypt.hinclude;__ImageBasedeclaredexternin C builds; C11 atomics when clang compiles mimalloc as C for an MSVC target; MSVC C compilation fixes; mingw fixes. - Other: arm64_32 support (PR #1444), Emscripten allocates its OS memory from the built-in allocator,
MI_ARENA_SLICE_SHIFTchoice on WASI, MSan awareness of memory written by raw syscalls, no syscalls on Android (CPython change), the weakstd::get_new_handlercan be left out of C builds, and the aligned-allocation fallback warning can be disabled; macOS exports the C++ operators so overridingnew/deleteworks when built as C; module maps for FreeBSD; arm32 and 32-bit test fixes. - Source reorganisation:
src/split intoalloc,arena,heap,prim,profileandutil,alloc-generic.csplit offpage.c,arena.candbitmap.crefactored, headers reorganised underinclude/mimalloc/, readme files added to source directories, and debug builds compile independently while release builds usestatic.c. - Workflows and CI were hardened; the
MI_OPT_FREE_SMALLCMake option's description changed;-Wpedanticwas dropped from the CMake warnings.
Full Changelog: v3.5.3+1...v3.6.0