MemGuard

Djaunt / MemGuard / macOS

Know before your Mac runs out of memory.

MemGuard watches the signals that actually move before macOS shows "your system has run out of application memory." It warns early, shows what is reclaimable, and never kills anything on its own.

Why the usual numbers lie

Two alarming numbers that mean nothing.

Activity Monitor and the out-of-memory dialog show figures that look like the problem and aren't. MemGuard ignores them and watches what actually moves first.

1,837 GBvirtual vs footprint781 MB

One VS Code extension host, measured. The huge figure is address space that V8 reserves for its pointer-compression cage. It is a promise to the allocator, not memory, and it cannot fill up.

Chrome reserves 89 TB of virtual space on the same machine and nobody blames it.

0.7 GBswap free, healthy

macOS keeps the swap file near capacity and grows it on demand. Free swap sits near zero on a healthy Mac. Measured: 0.7 GB free while total swap grew from 27 to 28 GB.

33–62%"free" before a crash

Across 41 recorded incidents the kernel's free-memory level read 33–62%, including three minutes before an out-of-memory dialog. It counts compressible pages as free.

131–137%load before that dialog

Memory load (swap used plus compressor, as a share of RAM) climbed for 30 minutes before the same dialog. After recovery it read about 104%. This is the number MemGuard warns on.

What it watches

Signals that move ahead of the dialog.

Sampled every 10 seconds by a launch agent. Apps are ranked by physical footprint, the number Activity Monitor's Memory column and the out-of-memory dialog both use.

SignalSource
Memory loadvm.swapusage, vm_stat, hw.memsize
Swap growthvm.swapusage over ~60s
Compressor sizevm_stat
Kernel pressurekern.memorystatus_vm_pressure_level
Free % (backstop)kern.memorystatus_level
Boot disk headroomswap cannot grow past it
One extension hostReading
footprint -p785 MB
top -stats mem785 MB
ps rss485 MB, 38% low

Tiers

Shape and color, so it reads either way.

The menu bar item labels itself with memory load, so you can read it without clicking.

OK

Below every threshold.

Warn

Load ≥ 120%, free ≤ 10%, or swap growing ≥ 1.5 GB/min. Posts a banner.

Critical

Load ≥ 130%, free ≤ 5%, kernel pressure critical, or boot disk ≤ 15 GB. Banner plus a dialog that stays until dismissed.

Paused

Relief and alerts silenced for a set time.

Escalates after 2 samples (~20s). De-escalates after 30 (~5 min). A late warning is useless, and an early all-clear is misleading. Alerts repeat every 10 minutes while a tier holds.

Safety

Your agents and builds are never touched.

Every agent, dev server, build, and SSH session runs inside a shell. MemGuard protects every shell subtree, so it excludes them by structure, not by a name list that goes stale.

claude← /bin/zsh← Code Helper (pty host)← Code Green: inside a shell subtree, protected. Verified ancestry of a running agent.
  • Automatic relief never terminates anything. Every kill is one explicit click.
  • Re-checked at click time. The target is validated against the protection rules again at the moment of the kill.
  • Only idle GUI renderers are candidates. Browser and Electron renderer helpers, which reload when the window is next focused. Editor extension hosts are excluded because they hold state.
  • Hard exclusions. Busy processes, root-owned processes, and PIDs below 500 are never candidates. Editor renderers are flagged risky and sorted last.

Relief ladder

What it can do about it.

The ladder ships conservative. Only the last rung frees real app memory, and it needs your click.

Purge off by default

Drops the file cache only. Pressure is usually anonymous app memory, which purge cannot free, and the cache refills in minutes. Kept as a manual menu item.

Pressure pulse opt-in

Signals critical pressure for ~3s so apps release internal caches, then resets. It writes a kernel sysctl, so it ships disabled.

Reclaim idle tabs manual

The action that frees memory. One click reclaims idle renderers, or pick one from a ranked list with app and megabytes for each.

Black box recorder

Evidence instead of a guess.

The out-of-memory dialog names a culprit, but nothing is measuring when it appears, so the claim can never be checked. On every escalation MemGuard writes a timestamped snapshot to ~/.djaunt/memguard/incidents/: per-app totals by physical footprint, the 25 largest processes with their protected or candidate status, and how much was reclaimable at that moment.

Install

No Homebrew. No dependencies.

Bash, launchd, and the system swiftc for the menu bar app. Everything lives in ~/.djaunt/memguard/.

Clone

git clone https://github.com/tory37/djaunt-memguard.git
cd djaunt-memguard/mac

Install the launch agents and menu bar app

./bin/djt-memguard install

Optional: passwordless purge

Grants exactly two commands, nothing else.

./bin/djt-memguard install-sudoers

CLI

Everything the menu does, in a shell.

djt-memguard
$ djt-memguard status            # current tier and all metrics
$ djt-memguard top               # what is reclaimable, what is protected
$ djt-memguard reclaim <pid>     # terminate one idle renderer, re-checked first
$ djt-memguard reclaim --safe    # reclaim safe candidates up to a target
$ djt-memguard relief            # drop the file cache
$ djt-memguard pause 60          # silence relief and alerts
$ djt-memguard log               # recent activity
$ djt-memguard uninstall         # unload and remove the launch agents

Platforms

macOS today. Laid out for more.

Each platform lives in its own top-level folder with its own probe, tiers, and UI. The ideas carry over. The signals do not.

macOS

supported

In mac/. Launch agent, CLI, and native menu bar app. Tested on Apple silicon.

Linux

not started

Would watch PSI (/proc/pressure/memory) and the OOM killer.

Windows

not started

Would watch commit charge against the commit limit.