atagen

monolith

public
Star0
Watch0

Commit 23f8719c411c

tarball

atagen <boss@atagen.co>2026-10-19 06:53 UTC

1 file changed+20-1
monolith: trim the heap after startup and client exits

glibc keeps freed chunks inside its heap rather than returning them to the kernel, and almost all of our heap is the NVIDIA driver's, whose startup temporaries left 2.3 MB resident. Those two moments free memory in bursts, so a malloc_trim then gives it back without a periodic cost.
Modified file: crates/monolith/src/state.rs+20 additions−1 deletions
@@ -26,6 +26,7 @@ use std::collections::HashMap;
2626 use std::ffi::OsString;
2727 use std::path::PathBuf;
2828 use std::sync::Arc;
29+use std::sync::atomic::{AtomicBool, Ordering};
2930 use std::time::{Duration, Instant};
3031
3132 pub struct State {
@@ -214,6 +215,9 @@ impl State {
214215 }
215216 self.wm.prune(now_ns());
216217 let _ = self.renderer.cleanup_texture_cache();
218+ if TRIM_HEAP.swap(false, Ordering::Relaxed) {
219+ trim_heap();
220+ }
217221 self.confirm_lock();
218222 self.complete_captures();
219223 if std::mem::take(&mut self.dirty.snapshot) {
@@ -509,5 +513,20 @@ pub struct ClientState {
509513
510514 impl ClientData for ClientState {
511515 fn initialized(&self, _client_id: ClientId) {}
512- fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}
516+ fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {
517+ TRIM_HEAP.store(true, Ordering::Relaxed);
518+ }
519+}
520+
521+/// Set at startup and whenever a client leaves, the two points that free a
522+/// burst of heap.
523+static TRIM_HEAP: AtomicBool = AtomicBool::new(true);
524+
525+/// glibc keeps freed memory inside its heap rather than returning it, which
526+/// measured 2.3 MB resident after startup alone.
527+fn trim_heap() {
528+ #[cfg(target_env = "gnu")]
529+ unsafe {
530+ libc::malloc_trim(0);
531+ }
513532 }