Repository navigation
GLM Metal 1.1.8: high idle memory use and repeated prefills, restart releases 44.5 GiB #1572
Description
Problem
With four long coding-agent sessions, TensorFold eventually reports memory pressure and repeatedly prefills most
of each prompt.
Two sessions had ended. With no running or queued requests, the process still used approximately 246 GiB.
A restart reduced memory use to 201.5 GiB with the same version and arguments.
Is this expected buffer retention, a missing release, or a leak?
Environment
- M5 Ultra, 256 GiB unified memory, macOS 27.0.1.
- TensorFold 1.1.8, Metal backend.
- Model:
TensorFold/GLM-5.3-Flash-MLX-oQ4-MTP. - No other substantial workloads.
Relevant settings:
GLM_LOAD_LIMIT_GB=240
GLM_MTP=1
--parallel 4 --context 1048576 --prompt-cache-gib 16
--learn-conversations --learn-gib 200 --spill-gib 200
--learn-daily-gib 1024 --learn-min-free-gib 20
--vision --kv-cache bf16 --prompt-precision bf16Observations
Measurements from October 29, 2026:
Before restart After restart
Time (CET) ~00:02 00:31:37
Running / queued requests 0 / 0 0 / 0
Process physical footprint 246.02 GiB 201.52 GiB
Prompt cache 5.36 GiB, 39 states Empty
macOS memory pressure Warning Normal
Disk swap Zero Zero
The new process had completed startup but had not processed any inference requests.
The restart released 44.51 GiB. The reported prompt cache accounts for only 5.36 GiB of that difference.
A separate 48-second idle sample showed approximately 246 GiB with normal memory pressure. High memory use did not
always trigger a warning.
Affected requests reused only 16,384 tokens from prompts of approximately 102,000 to 170,000 tokens. Other
requests still achieved deep cache reuse.
TensorFold logged:
warning: memory pressure: warning: one more stream only when none runs, no new kept prompt states, the prompt
cache sheds toward 8192 MiB
Relevant checks
- Startup reported approximately 200.7 GiB of retained allocations.
- Startup confirmed that all four streams share one MLA context pool, rather than four full context pools.
- The daily SSD write ledger showed approximately 75 GiB of the 1024 GiB allowance.
- The disk cache was near its 200 GiB limit. TensorFold normally makes room through eviction.
- The current process logged no
DailyBudgetSpentorOverCaperrors. - The current process also logged 101
StagingFullerrors. These concern a separate write-staging limit and do
not establish the memory cause.
The 240 GB load setting is not a hard process-memory limit.
Expected behavior / questions
I understand that the pressure guard intentionally prevents new prompt states. However, this also prevents those
new states from reaching the SSD cache.
What owns the additional memory after requests finish? Can TensorFold release or reuse it before sustained
pressure prevents new prompt states?
Activity
ashhart commentedon Oct 29, 2026
/atc claim
ashhart commentedon Oct 31, 2026
Thanks @wSzki for the before and after numbers. They show 44.51 GiB that the prompt cache (5.36 GiB) doesn't explain.
What the log line means: at warning pressure the server lets a new stream start only when none runs, keeps no new
prompt states, and sheds the prompt cache toward half its budget (8,192 MiB of your 16 GiB). That is why later turns
resumed at only 16,384 tokens: their newer states were never kept. So the repeated prefills follow from the pressure,
and the question is what holds the extra memory once requests end. We don't know that yet.
To narrow it down, could you send:
--learn-conversationsand--spill-gib, to tell their buffers apart from the engine's;vmmap --summary <pid>before a restart, if you can.On the
StagingFullerrors: on main, in 1.2.0, a save that fits the staging permit waits for a write in flight insteadof being skipped (#1548). That won't free memory, but more states should reach the disk.
wSzki commentedon Nov 1, 2026
Fresh data from a new process: the growth replicated, plus the evidence you asked for. Sections 1-3 are v1.1.8 (Metal, M5 Ultra 256 GiB, flags unchanged from the original report, process launched 2026-10-31 02:02Z; all timestamps UTC). The owner has since upgraded to 1.2.0; a follow-up on that version is at the end.
Growth, fresh process (1.1.8)
… would take its residency set past the 223.5 GiB load limit: it stays outrefusals; the admission guard held throughout. Swap flat at 118 MiB; preemptions 0.1) Footprint after each session ends
15 s series (footprint, connections, completed-request count, kernel pressure). Inter-request idle windows (done-count plateau ≥45 s, conn=0, dtps=0, same PID):
No material drop within any window (flat to slightly higher), +32.5 GiB above the 02:04 idle baseline. Caveat: these are ~45–60 s gaps, not captured session terminations, so per-turn vs per-session stays open with this data. (The 1.2.0 follow-up below adds hour-long windows.)
3) vmmap --summary before restart
Captured at the grown state (08:41:15Z, launch 02:02:26Z, footprint 230.7G, peak 231.4G); raw output inline below. Key numbers from it:
IOAccelerator (graphics): 232.5G resident in that family across 326 regions.IOAcceleratorfamily (90 regions) carries only 112K dirty.vmmap --summary 25834: 2026-10-31 09:41:15 CET (launch 03:02:26, footprint 230.7G, peak 231.4G)
2) Run without --learn-conversations / --spill-gib
Not run yet.
Follow-up: same shape on 1.2.0
The owner restarted the process for the 1.2.0 upgrade at 12:44Z (not forced for this report; the 1.1.8 process above was still live and being sampled until then). Same flags and model.
Largest footprint decline inside any of the 23 windows: 0.20 GiB. All net increase appeared across the active periods between windows (202 → 208 → 217 → 239.4 GiB), and the footprint never returned toward the 202 GiB floor in any sampled window. So no material drop in any of these sampled idle windows, three of them 59 minutes long. What stays open with this data is per-turn vs per-session accumulation, since the steps line up with sessions.
warning: prompt cache: imprint: saving 82861 tokens skipped (StagingFull). Not claiming whether these fit the staging permit; reporting that skips still occur on 1.2.0.Environment: 1.1.8 (sections 1-3) and 1.2.0 (follow-up), Metal, M5 Ultra 256 GiB,
--parallel 4 --context 1048576 --prompt-cache-gib 16 --learn-conversations --learn-gib 200 --spill-gib 200 --learn-daily-gib 1024 --max-tokens 65536 --kv-cache bf16 --prompt-precision bf16 --vision --vision-max-images 16,GLM_LOAD_LIMIT_GB=240.ashhart commentedon Nov 1, 2026
Thanks @wSzki, the full vmmap and timings helped. We found one definite accounting bug that fits your numbers: with
--learn-conversations, a GLM prompt state restored from disk was charged as a small resident save (its KDA states) while the restore allocates its whole attention image, so the prompt cache could hold about 2.5 GB more per restored state at 130,000 tokens than its budget allowed. That matches growth during active periods that stays through idle, as you saw. The fix is #1818. We'll run GLM on our M3 Ultra with your flags before it lands, and say here whether it accounts for all of the growth or only part.