StackTraces
DeepSeek已验证

ollama run deepseek-r1:14b 报 out of memory

@debug_master11 天前更新阅读 6 min00

拉完 deepseek-r1:14b/32b/70b 后第一次推理就 OOM,不一定是模型文件大小问题。三件常被忽略的事叠在一起:默认 keep_alive = 5m 让上一个模型继续占着内存;默认 num_ctx = 4096 推高 KV cache;OLLAMA_MAX_LOADED_MODELS 默认允许多个模型并发。把它们都收住,多数机器都能救回来。

Issue & Env(案发现场与环境)

  • 业务场景:本地拉完 deepseek-r1:14b 或 deepseek-r1:32b,跑 ollama run 立刻被 kill,Linux 上 dmesg 报 oom-kill;有时换了一个更小的 tag 反而没事。
  • 环境:Ollama v0.34.x,DeepSeek-R1 蒸馏模型 Q4_K_M 量化(默认体积 1.5b=1.1GB / 7b=4.7GB / 8b=5.2GB / 14b=9.0GB / 32b=20GB / 70b=43GB),官方上下文窗口统一 128K,671b 主模型 160K。

StackTrace(堆栈日志)

CUDA 报:

Error: CUDA error: out of memory

进程被系统 OOM killer 干掉后,Linux 内核日志会出现:

ollama invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

500 形态(服务层把推理进程的死亡包了一下):

500 Internal Server Error: memory layout cannot be allocated

看到这些,先翻 journalctl -u ollama -n 200 --no-pager,里面会有 Ollama 估算的可用显存、模型大小、KV cache 占用分配。

Reproduce(复现代码)

最小复现路径:

# 1) 拉一个比显存/内存大的 tag
ollama pull deepseek-r1:32b       # 20GB

# 2) 先让另一个模型占着内存(默认 keep_alive 5 分钟)
ollama run deepseek-r1:8b --verbose ""  # 然后 bye,模型还在内存里

# 3) 五分钟内再 run
ollama run deepseek-r1:32b
# Error: model requires more system memory than is available
# 或者直接 CUDA error: out of memory

显存预算粗估:Q4_K_M 量化约 0.6 GB × 十亿参数 + KV cache。14b 想要 ~10GB VRAM,32b 想要 ~20GB,70b 想要 ~43GB。ollama ps 的 SIZE 列加上 VRAM 列就是实际占用。

Root Cause(根因分析)

OOM 不是一个原因,是多个原因叠在一起。

一、模型本身超过 VRAM/RAM

DeepSeek-R1 蒸馏家族覆盖 1.5b 到 70b,磁盘体积跟运行时占用几乎是 1:1(Q4_K_M 量化需要近乎线性把权重读到显存)。32GB 内存的机器硬上 32b,能跑但要靠 swap;16GB 上 14b 已经吃紧,70b 直接死。

二、keep_alive 默认 5 分钟,旧模型没卸掉

官方 FAQ:By default models are kept in memory for 5 minutes before being unloaded. 这意味着 5 分钟内连续 run 两个不同模型,两个都会占内存。ollama ps 会列出当前驻留的全部。OLLAMA_MAX_LOADED_MODELS 默认是 3 × GPU 数(CPU 推理默认 3),多个模型并发时会同时驻留。

三、num_ctx 4096 默认值推高 KV cache

Ollama 全模型统一默认 num_ctx = 4096,DeepSeek-R1 蒸馏家族最大上下文是 128K。不改默认情况下,KV cache 占的额外显存跟上下文长度线性相关。4096 时还能塞进普通机器,8192/16384 起步就开始挤;128K 配 70b,KV cache 就要 80GB+。很多人改了 num_ctx 后第一次跑就崩,其实是把 KV cache 顶上去的。

四、Q4_K_M 量化对精度的取舍

Ollama 库页发的是 Q4_K_M(默认)、Q4_K_S、Q5_K_F/Q5_K_M/Q8_0 等不同量化。Q4_K_M 是体积/质量甜点,但 Q8_0 同样模型能膨胀 1.5–1.7 倍。手动拉 Hugging Face 上的 GGUF 改 Q8_0 时容易撞 VRAM。

五、容器/W SL/内存统计口径不一致

WSL2 默认 VM cap 是 50% 主机内存或 8GB 取较小值。64GB 主机跑在 WSL2 里没改 .wslconfig,Ollama 看到的是 8GB。Docker 容器 / K8s pod 有 --memory 或 resources.limits.memory 上限,Ollama 只能看到容器内的额度,宿主机再多也帮不上忙。ZFS 文件系统上 ARC 缓存会刻意吃满空闲内存,让 Ollama 误判可用 RAM。

The Fix(解决方案)

按便宜程度排序,先试不花钱的。

第一步:先看现在占着多少

ollama ps

输出形如:

NAME                ID          SIZE     PROCESSOR  UNTIL
deepseek-r1:8b      ...         5.5 GB   100% GPU   4 minutes from now

UNTIL 列就是 keep_alive 倒计时。

第二步:手动清掉旧模型

ollama stop deepseek-r1:8b
# 或者接口里 keep_alive=0
curl http://localhost:11434/api/generate -d '{"model":"deepseek-r1:8b","keep_alive":0}'

第三步:把 num_ctx 收紧

num_ctx 是 KV cache 的最大头。不需要长上下文就先收小:

# 临时
ollama run deepseek-r1:14b --num-ctx 2048

# 永久(Modelfile)
cat > Modelfile <<'EOF'
FROM deepseek-r1:14b
PARAMETER num_ctx 2048
EOF
ollama create deepseek-r1:14b-2k -f Modelfile
ollama run deepseek-r1:14b-2k

或者全局把默认值调小:服务侧 OLLAMA_CONTEXT_LENGTH=2048。DeepSeek-R1 推荐 temperature 0.6、top_p 0.95,跟默认就够,num_predict 不需要单独压。

第四步:让 keep_alive 短一点或彻底卸掉

OLLAMA_KEEP_ALIVE=0 让模型用完立即释放;想要快速重新加载就 -1 一直驻留:

# systemd drop-in
sudo systemctl edit ollama
[Service]
Environment="OLLAMA_KEEP_ALIVE=0"
sudo systemctl daemon-reload && sudo systemctl restart ollama

第五步:把最大驻留模型数压到 1

[Service]
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Environment="OLLAMA_NUM_PARALLEL=1"

这样 Ollama 必须先卸旧模型才能加载新的,永远只有一个占内存。多请求并发在队列里等。

第六步:换更小的量化档或更小的 tag

显存吃紧时优先选更小的 tag,而不是上 Q4_K_S(不一定发布)或 Q8_0(更大)。deepseek-r1:14b 用 Q4_K_M ~9GB;deepseek-r1:7b 走 Q4_K_M 是 4.7GB,16GB 内存机器也能跑。

GPU 实在不够就回退到纯 CPU:Modelfile 加 PARAMETER num_gpu 0,把模型强制留在内存里运行,速度慢但稳。

第七步:环境侧的内存可见性

# /etc/modprobe.d/zfs.conf   (限制 ZFS ARC)
options zfs zfs_arc_max=8589934592

# %UserProfile%\.wslconfig   (WSL2 内存上限)
[wsl2]
memory=24GB
swap=8GB

然后 wsl --shutdown 重启 WSL2。Docker / K8s 同理上调容器 memory。

按这套顺序走一遍,out of memory 一般都能收敛。如果还崩,说明机器硬件确实跑不动这个 tag,往下一档换即可。

References(参考来源)

最后更新于 2026-09-30

这篇帮到你了吗?

刚解决了一个棘手的报错?花两分钟记录下来,帮助下一个遇到同样问题的开发者。

贡献一条解法