diff --git a/internal/gui/sessions.go b/internal/gui/sessions.go index f9fe3c44..c89a6fb6 100644 --- a/internal/gui/sessions.go +++ b/internal/gui/sessions.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "runtime" + "runtime/debug" "strconv" "strings" "sync" @@ -320,12 +321,27 @@ func warmSessions() { } go func() { time.Sleep(3 * time.Second) - statsFor(0) - statsFor(30) - usage.QueryPage(usage.All, usage.Filter{}, 0, 50) + warmUp( + func() { statsFor(0) }, + func() { statsFor(30) }, + func() { usage.QueryPage(usage.All, usage.Filter{}, 0, 50) }, + ) }() } +// warmUp runs the warm-up's reads, then hands what they threw away back to +// the system at once. Reading a long history allocates many times what it +// keeps (a 13k-session Codex and Claude Code history: ~830 MB allocated to +// keep ~80 MB of indexes), and Go's scavenger returns those pages only over +// the next minutes: a magpie that had just started read ~470 MB for its +// first two minutes at rest. +func warmUp(steps ...func()) { + for _, step := range steps { + step() + } + debug.FreeOSMemory() +} + // openTerminal runs a command in the chosen Mac terminal through a .command // file, as it would open from Finder: no Automation consent. // The shell is left open when the agent quits. diff --git a/internal/gui/sessions_warm_test.go b/internal/gui/sessions_warm_test.go new file mode 100644 index 00000000..97b68f1a --- /dev/null +++ b/internal/gui/sessions_warm_test.go @@ -0,0 +1,28 @@ +package gui + +import ( + "runtime" + "testing" +) + +// The warm-up's garbage goes back to the system when it ends, not over the +// scavenger's next minutes: an idle magpie that just started read ~470 MB. +func TestWarmUpReturnsItsGarbage(t *testing.T) { + var keep [][]byte + warmUp(func() { + // a read that allocates far more than it keeps + for range 256 { + keep = append(keep, make([]byte, 1<<20)) + } + keep = [][]byte{keep[0]} + }) + var m runtime.MemStats + runtime.ReadMemStats(&m) + if held := m.HeapIdle - m.HeapReleased; held > 32<<20 { + t.Fatalf("after the warm-up %d MB of freed heap is still held from the system", held>>20) + } + if m.HeapAlloc > 128<<20 { + t.Fatalf("after the warm-up %d MB of heap is still in use", m.HeapAlloc>>20) + } + runtime.KeepAlive(keep) +}