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) #1226.69 GB已加载的模型
Python (mlx_lm.server) #2188.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}%),等待释放..."
fi

3. LaunchAgent ThrottleInterval

从 10s 改为 60s,防止崩溃后快速重启造成循环。


内存预算参考

模型磁盘运行时加载峰值512GB 余量
M2.5 4-bit120 GB~150 GB~250 GB✅ 充足
M2.5 8-bit230 GB~270 GB~400 GB⚠️ 紧张

教训

  1. 大模型内存释放不是瞬间完成的 — Metal GPU buffer 需要时间回收
  2. 重启大模型服务必须等进程完全退出 — sleep 2 不够,要轮询确认
  3. ThrottleInterval 太短会造成崩溃循环 — 60s 是安全值
  4. 统一内存是优势也是风险 — CPU 和 GPU 共享同一块内存,一个吃太多全部遭殃

这次事故之后,ai-switch.sh 和 ai-services.sh 都加了完整的等待逻辑。再也没有发生过 OOM Panic。