2026-01-17 · Engineering
The macOS Memory Tunables Apple Doesn't Tell You About
You have 32GB of RAM. Activity Monitor shows half as "inactive" and "file cache." You launch a third instance of your AI coding assistant and suddenly you're swapping. What gives?
The Problem
Apple's philosophy: "unused RAM is wasted RAM." The kernel caches aggressively, betting you'll need those files again. In theory, cache is "free" — evictable on demand.
In practice, "on demand" has a cost. When you spike memory usage, macOS has to compress pages, evict cache, and sometimes swap — all while you wait.
Finding the Knobs
While investigating why three Claude Code instances caused swap pressure on an M4 Mac Mini with 32GB, I found these kernel parameters:
$ sysctl kern.vm_page_free_target kern.vm_page_free_min
kern.vm_page_free_target: 4000
kern.vm_page_free_min: 3500
macOS uses 16KB pages. Those defaults translate to:
- free_target: 64MB — the desired free memory
- free_min: 56MB — threshold to wake the pageout daemon
On a 32GB machine, that's 0.2% headroom. The kernel doesn't start proactively reclaiming until you're nearly out.
What They Do
From the XNU kernel source (osfmk/vm/vm_page.h):
vm_page_free_target /* How many pages do we want free? */
vm_page_free_min /* When to wakeup pageout */
The pageout daemon sleeps until free pages hit these thresholds. With defaults this low, it's reactive — scrambling when you're already in trouble rather than staying ahead.
The Fix
Increase the thresholds for proactive reclamation:
# 4× default: ~256MB headroom
sudo sysctl kern.vm_page_free_target=16000 kern.vm_page_free_min=12000
# 8× default: ~512MB headroom
sudo sysctl kern.vm_page_free_target=32000 kern.vm_page_free_min=24000
Verify:
$ sysctl kern.vm_page_free_target kern.vm_page_free_min
kern.vm_page_free_target: 16000
kern.vm_page_free_min: 12000
Results
Tested on macOS 15 Sequoia, M4 Mac Mini (32GB):
| Test | Result |
|---|---|
| Writable without SIP changes | ✓ |
| Takes effect immediately | ✓ |
| Survives sleep/wake | ✓ |
| Survives reboot | ✗ |
To persist, create /etc/sysctl.conf:
sudo tee /etc/sysctl.conf << 'SYSCTL'
kern.vm_page_free_target=16000
kern.vm_page_free_min=12000
SYSCTL
Monitoring
Check if it's helping:
# Memory pressure (should stay green more often)
memory_pressure | grep "free percentage"
# Watch compression vs swap ratio
vm_stat | grep -E "(Compressions|Swapouts)"
You want high compressions (fast, in-RAM) and low swapouts (slow, disk I/O). The tuning should shift the ratio toward compression.
Why So Low by Default?
Apple optimizes for the common case: 8-16GB machines, light workloads. Maximum cache means faster app launches. The occasional swap spike is acceptable.
If you're running Docker, simulators, multiple Electron apps, or AI tooling — you're not the common case.
The Nuclear Option
sudo purge
Clears inactive memory immediately. But it's one-shot — cache refills. The sysctl approach maintains ongoing headroom.
Caveats
- Undocumented: Apple could change these any time
- Trade-off: Less cache means potentially slower cold launches
- Not magic: Won't fix genuinely insufficient RAM
Related Tunables
Other interesting sysctls (read-only on modern macOS, but worth checking):
sysctl kern.vm_page_speculative_percentage # speculative cache limit
sysctl kern.vm_page_speculative_q_age_ms # how long speculative pages live
macOS memory management is good. The defaults just assume you want maximum caching. If you've got RAM to spare and hate swap latency, now you know where the knobs are.
Tested January 2026, macOS 15 Sequoia, Apple M4.
See also: How We Found macOS Memory Tunables by Arguing with an AI — the story of how this was discovered