Metal OOM 内核 Panic
2026-02-14 · Mac Studio 突然死机 · SoC 硬件看门狗强制重启
发生了什么
运行 MiniMax M2.5 4-bit 模型期间,Mac Studio 突然死机。不是应用崩溃,是内核 Panic —— SoC 硬件看门狗检测到系统完全无响应,强制重启。
code
sysctl kern.shutdownreason
# → "wdog,reset_in_1" — 硬件看门狗触发根本原因
直接原因
ai-switch.sh 切换模型时,pkill 旧进程后仅等待 2 秒就启动新进程。
200GB+ 的大模型进程释放 Metal/GPU 内存远不止 2 秒。两个进程同时存在:
| 进程 | 内存 | 说明 |
|---|---|---|
| Python (mlx_lm.server) #1 | 226.69 GB | 已加载的模型 |
| Python (mlx_lm.server) #2 | 188.00 GB | 正在加载的新模型 |
| 其他 | ~20 GB | 系统 + 浏览器 |
| 合计 | ~435 GB | 仅剩 0.1 GB 空闲 |
为什么 120GB 模型需要 200GB+ 内存?
code
模型磁盘大小: 120 GB (27 个 safetensors 分片)
↓
mx.load() mmap: ~120 GB (虚拟内存映射)
+
Metal GPU buffer: ~130 GB (GPU 计算用的权重副本)
=
单进程峰值: ~250 GB
两个进程同时加载: ~500 GB → 超出 512GB → 💥 Panic
macOS 内存保护机制
code
内存压力上升
↓
① 压缩器 → 压缩不活跃内存页
↓
② Jetsam → 杀掉低优先级进程
↓
③ Swap → 写入 SSD
↓
④ SoC Watchdog → 硬件强制重启(内核 Panic)
本次直接跳到第 ④ 步 —— 两个 200GB+ 进程同时加载,内存耗尽速度太快,Jetsam 来不及介入。
修复
1. pkill 后轮询等待
替代原来的 sleep 2:
code
pkill -f "mlx_lm.server" 2>/dev/null
WAIT_SEC=0; MAX_WAIT=60
while pgrep -f "mlx_lm.server" > /dev/null 2>&1; do
if [ $WAIT_SEC -ge $MAX_WAIT ]; then
echo "⚠️ 旧进程 60s 未退出,强制 kill -9"
pkill -9 -f "mlx_lm.server" 2>/dev/null
sleep 3; break
fi
sleep 1; WAIT_SEC=$((WAIT_SEC+1))
done
sleep 3 # 额外等待 Metal 内存释放2. 启动前内存检查
code
FREE_PCT=$(memory_pressure | grep "free percentage" | grep -o '[0-9]*')
if [ "$FREE_PCT" -lt 30 ]; then
echo "⚠️ 内存不足 (${FREE_PCT}%),等待释放..."
fi3. LaunchAgent ThrottleInterval
从 10s 改为 60s,防止崩溃后快速重启造成循环。
内存预算参考
| 模型 | 磁盘 | 运行时 | 加载峰值 | 512GB 余量 |
|---|---|---|---|---|
| M2.5 4-bit | 120 GB | ~150 GB | ~250 GB | ✅ 充足 |
| M2.5 8-bit | 230 GB | ~270 GB | ~400 GB | ⚠️ 紧张 |
教训
- 大模型内存释放不是瞬间完成的 — Metal GPU buffer 需要时间回收
- 重启大模型服务必须等进程完全退出 — sleep 2 不够,要轮询确认
- ThrottleInterval 太短会造成崩溃循环 — 60s 是安全值
- 统一内存是优势也是风险 — CPU 和 GPU 共享同一块内存,一个吃太多全部遭殃
这次事故之后,ai-switch.sh 和 ai-services.sh 都加了完整的等待逻辑。再也没有发生过 OOM Panic。