Mimalloc Cigarette: Losing one week of my life catching a memory leak (Rust)
Garbage Collection vs Manual Memory Management
- Debate over GC: some see it as essential for complex, shared object graphs; others report spending more time fighting GC (tuning, disabling, pooling) than manual memory would have cost.
- Latency-sensitive domains (VR, games, trading) are cited as especially painful with GC pauses and unpredictable behavior; Unity’s GC is called out as particularly bad.
- Counterpoints: modern GCs (e.g., on the JVM) can be nearly pauseless for many workloads; GC problems are often intertwined with other language design choices (type system, JIT strategy, value types, forced heap allocation).
- Newer systems programmers increasingly prefer automatic resource management; others insist that for systems work, opaque runtimes and non-negotiable GCs are unacceptable.
mimalloc, Thread-Local Allocators, and Rust
- Core technical issue: mimalloc keeps freed memory tied to the allocating thread and reclaims it lazily. Cross-thread free patterns can look like leaks until the original thread allocates again or
mi_collectis called. - Rust’s
mimalloccrate wires it in as the global allocator but doesn’t exposemi_collect; the lower-levellibmimalloc-sysdoes. - This is framed as a design tradeoff: per-thread optimization vs cross-thread behavior. It’s argued this could bite C/C++ too and is fundamentally an architectural problem.
Language and Allocator Design
- Critique that Rust and C++ hide allocation behind global allocators, discouraging explicit, domain-specific allocator thinking.
- Zig is praised for explicit allocator passing, trading convenience for control and predictability.
- Suggestions to design languages more around
mmap/munmapand pages are met with skepticism; page sizes and concurrency concerns make higher-level allocators necessary. - Embedded and high-performance contexts often avoid dynamic allocation entirely, using bump or arena allocators.
Debugging War Stories and Reliability
- Multiple anecdotes of long-running, hard-to-reproduce bugs: UI drag-and-drop issues, stale client state in localStorage, JIT miscompilations, uninitialized locals.
- Emphasis on reproducibility and the scientific method in debugging; warnings against declaring bugs “fixed” without reproduction.
- Some languages (e.g., D) choose default initialization of locals to avoid entire classes of memory bugs.
Other Side Topics
- Brief side debate on using floating point for money: some insist on integer cents; others use floats (even single-precision) for modeling/backtesting with controlled rounding.
- Observations that allocators and OS resource management (memory, filesystems, file descriptors) have GC-like aspects but important differences in visibility and control.