1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//! Allocator-level helpers.
//!
//! The TUI refresh loops allocate and drop large numbers of small objects
//! each cycle (session-file JSONL parsers, per-model hashmaps, ratatui row
//! vectors). With the default glibc allocator this leaves arenas full of
//! freed-but-not-returned pages, which is what drives the monotonic RSS
//! growth you see on a long-running `vibe_coding_tracker usage` session.
//!
//! `release_freed_heap` calls `malloc_trim(0)` on Linux/glibc to ask the
//! allocator to give those pages back to the kernel. It is a no-op on
//! other platforms (musl, macOS, Windows) because the symbol isn't
//! available — those allocators either return memory eagerly already or
//! don't expose a trim knob.
/// Ask the system allocator to release any free pages in its arenas back
/// to the OS. Safe to call as often as you like — cost is O(arena size).
/// Apply one-time glibc malloc tuning. Must be called before the first
/// allocation that crosses thread boundaries to have its full effect.
///
/// What it does (Linux glibc only; no-op elsewhere):
///
/// - `M_ARENA_MAX = 2`: cap the number of per-thread arenas glibc will
/// create for multi-threaded workloads. Without this cap, a 16-core box
/// can spin up to 128 arenas for our Rayon worker pool; each arena
/// retains its own free list independently of `malloc_trim`, which is
/// how the TUI grew ~6 MB per 10 s refresh even after we trimmed the
/// main arena at the end of every cycle. Two arenas is enough to keep
/// allocator lock contention off the critical path while preventing the
/// retention from multiplying across cores.
/// - `M_TRIM_THRESHOLD = 128 KiB`: lower the threshold at which glibc
/// will voluntarily hand the arena's top chunk back to the OS. Default
/// (128 KiB) is already low but the value can grow automatically; we
/// pin it so long sessions don't drift.