Skip to main content

pond/
memory.rs

1//! Allocator maintenance. glibc parks freed pages on its per-arena free lists
2//! rather than returning them, so a process that peaked stays resident at that
3//! peak long after the peak's memory is free (#61, #245). `malloc_trim` asks
4//! for the top of each arena back.
5
6/// Ask glibc to return freed pages to the OS. Call only from a quiescent
7/// point: glibc walks and locks every arena in turn, so a caller mid-workload
8/// pays for the walk and stalls every concurrently allocating thread.
9///
10/// The `released` flag is glibc's own report of whether anything came back; it
11/// is logged rather than acted on, because "nothing to release" is the normal
12/// steady-state answer, not a failure.
13// `target_env = "gnu"` and not bare `target_os = "linux"`: `malloc_trim` is a
14// glibc extension that the `libc` crate declares only for the gnu environment,
15// so a musl target would not even compile against the branch below.
16#[cfg(all(target_os = "linux", target_env = "gnu"))]
17#[expect(unsafe_code, reason = "malloc_trim FFI")]
18pub fn trim_allocator() {
19    // SAFETY: malloc_trim has no caller-side safety invariants.
20    let released = unsafe { libc::malloc_trim(0) != 0 };
21    tracing::debug!(released, "trimmed glibc allocator");
22}
23
24/// No-op off glibc - musl, the macOS allocator and the Windows CRT have no
25/// equivalent entry point, and none of them retain pages the way glibc does.
26#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
27#[inline]
28pub fn trim_allocator() {}
29
30#[cfg(test)]
31mod tests {
32    /// The FFI branch only has to exist and return; asserting on RSS here
33    /// would be asserting on the kernel's bookkeeping, not on this code.
34    #[test]
35    fn allocator_trim_is_callable() {
36        super::trim_allocator();
37    }
38}