You load a 7B model, ask something, get an answer. You switch to a 13B model for a harder question. Now everything is slow, including the first model when you switch back, and including applications that have nothing to do with either.
The usual cause is simple: both models are still in memory. Switching models in the interface does not necessarily unload the previous one.
Resident by default
Loading a model is expensive. Weights have to be read from disk and placed in memory, which takes seconds to tens of seconds depending on size. If a runtime unloaded after every response, every follow-up question would pay that cost again.
So runtimes keep models resident. LM Studio keeps a loaded model in memory until something unloads it, and exposes an idle TTL to automate that: lms load --ttl <seconds> unloads the model after that many seconds without use. Ollama's FAQ states the default plainly: models are kept in memory for 5 minutes before being unloaded, configurable through the keep_alive parameter, with reload on demand.
This is the right default for a single model and a surprising one for two. Nothing in the interface necessarily tells you that the first model is still occupying memory while the second one loads.
Check what is actually resident rather than assuming. Both runtimes ship a command for it:
lms ps # LM Studio
ollama ps # OllamaOn a machine with nothing loaded, lms ps reports that no models are currently loaded. If it lists two, that is your answer.
Why this hurts more on Apple Silicon
There is one pool of memory shared by the CPU and GPU. Model weights, the KV cache, your browser, and the window server all draw from the same 16, 24, 48, or 128 GB. There is no separate video memory that absorbs the model and leaves system memory untouched.
Two resident models therefore cost the sum of their weights out of the same budget everything else is using. A 4 GB model plus an 8 GB model is 12 GB gone before counting KV caches, which grow with context length and are held per model. That growth is a property of the architecture rather than of the runtime: self-attention relates every position in a sequence to every other position, as set out in Attention Is All You Need.
Weights also do not compress usefully. macOS has a memory compressor that absorbs pressure well for ordinary application data. In testing on the machine used here, 8.59 GB of zero-filled allocation produced only 3.75 GB of working-set growth, because the compressor crushed it. Model weights are high-entropy and will not behave that way.
That does not mean the resident cost equals the file size. It was lower than the file on the machine measured below, and how much lower depends on the runtime. Do not size from the file listing alone.
The failure sequence
What you experience as a gradual slowdown is macOS working through an escalating series of responses.
First it reclaims cached pages. File cache, speculative reads, and purgeable memory are released to make room. This is free and invisible, and it is why free memory dropping to near zero is not by itself a problem.
Then it compresses. Less-recently-used pages are compressed in place, which costs CPU but avoids disk entirely. Still no stall you would notice.
Then it swaps. Pages are written to disk, and any page that is needed again has to be read back before the thread waiting on it can proceed. This is the point at which the machine feels broken, and it is the first stage that produces a signal you can watch directly.
The important consequence is that swap traffic is not an early warning. By the time it appears, the cheaper mechanisms are already exhausted.
Confirming it is memory and not something else
Three checks, in order. The first two need nothing installed.
1. Is swap actually moving
vm_stat | grep -E "Swapins|Swapouts"Run this twice a few seconds apart and subtract. These are the swapins and swapouts fields of vm_statistics64, declared in XNU's osfmk/mach/vm_statistics.h. They are cumulative page counts, so the difference between two readings is what matters, and a large standing total with no change between readings means the pressure has passed. Multiply the difference by the page size to get bytes, which is 16384 on Apple Silicon and worth confirming with sysctl hw.pagesize rather than assuming.
Sustained non-zero swap traffic confirms the diagnosis. A large vm.swapusage figure with no movement does not, because swap allocation is sticky and lingers long after the pressure that created it.
2. What does the kernel think
sysctl kern.memorystatus_vm_pressure_level
# 1 = normal, 2 = warning, 4 = criticalThose are the same three levels Apple exposes to applications through a memory pressure dispatch source.
3. What is resident
lms ps
ollama psIf both models are listed and swap is moving, you have found it.
Fixes, in order of effect
Unload the model you are not using. This is the direct fix and it is immediate.
lms unload <model>
ollama stop <model>Set an idle timeout. If you switch between models often, having models unload themselves trades reload time for headroom. LM Studio takes --ttl at load time; Ollama exposes a keep-alive duration.
Reduce context length. The KV cache is held per model and grows with context. Two models with long conversations cost considerably more than two models with short ones.
Do not size against free memory. Free memory is close to zero on a healthy Mac by design. Available memory, which counts reclaimable cached pages, is the figure that tells you whether another model will fit.
Measured: two models resident at once
Everything above is the documented mechanism. This is one machine actually driven into it.
Measured on a MacBook Pro, Apple M5 Pro (5 Super and 10 Performance cores), 48 GB, macOS 27.0 build 26A5425a, Searoom 0.3.0, with LM Studio and both models at a 4096 token context. The models were Devstral Small 2 24B Instruct at Q4_K_M in GGUF, a 14.33 GB weights file, and Gemma 4 26B A4B QAT in MLX 4-bit, 15.62 GB of safetensors. Figures come from vm_stat and Searoom --dump-sample.
| State | Working set | Free | Compressor | Swap out | Kernel level |
|---|---|---|---|---|---|
| Nothing loaded | 22.05 GB | 10.29 GB | 0.82 GB | 0 | 1 normal |
| First model | 33.55 GB | 1.83 GB | 0.82 GB | 0 | 1 normal |
| Second model loading | 42.70 GB | 0.05 GB | 17.17 GB | 16 pages | 2 warning |
| Both resident | 43.69 GB | 0.56 GB | 4.47 GB | 13,756 pages | 1 normal |
The escalation is the whole story, and it runs left to right. Free memory is spent first, falling from 10.29 GB to 0.05 GB. The compressor takes the next share, peaking at 17.17 GB. Only after both are exhausted does anything reach disk: 13,756 pages at 16384 bytes each, which is 225 MB written out.
Three results from that run are worth more than the trace itself.
Swap-out happened and nothing stalled. swapins stayed at exactly zero for the entire time both models were loaded. Pages were written out and never read back, so no thread ever waited on one. This is the distinction the swap article draws, and it cuts both ways: swap-out alone is the machine tidying up, and it is swap-in that produces the pause you feel.
The first model cost less than its file. Loading Devstral moved the working set by 11.50 GB against a 14.33 GB weights file, about 80% of it. The likely reason is that llama.cpp memory-maps GGUF weights, so the pages are file-backed and need not all be resident at once. Gemma, running on MLX rather than llama.cpp, behaved differently. The practical rule is that resident cost depends on the runtime and the format, so measure it rather than reading the file size.
The kernel disagreed with the working set. At the worst moment the kernel reported level 2, warning, for a single sample. With both models settled at a 43 GB working set and 225 MB on disk, it was back to 1, normal. A machine can be doing something drastic while the kernel considers the situation handled.
Limits of this measurement
One machine, one macOS build, one pair of models, one context length. A 48 GB Mac has to be pushed hard to reach this state: the article's own example of a 4 GB model beside an 8 GB model would not come close here, and reproducing the failure took roughly 30 GB of weights. On a 16 GB or 24 GB machine the same sequence arrives far sooner and with much less provocation. The numbers above are a demonstration that the mechanism is real, not a prediction of what your Mac will do.
The distinction between swap allocation and swap throughput is covered in swap I/O vs swap size, why available memory beats free memory in what macOS memory pressure actually measures, and the shared-pool ceilings in unified memory.
Sources
- Idle TTL and auto-evict, LM Studio documentation
- lms load, LM Studio documentation
- Ollama FAQ
- lms, LM Studio's CLI
- Vaswani et al., Attention Is All You Need (2017)
- osfmk/mach/vm_statistics.h, XNU source
- DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, Apple Developer Documentation
- Devstral Small 2 24B Instruct, model card