MLX runner: KV-cache memory accumulates per-request on dense models (independent of context size/tools), not released short of restart

在 Ollama 的 MLX runner 中,当使用 dense(非混合架构)模型时,进程常驻内存会随请求次数单调增长,与上下文长度或工具 schema 无关;在长会话中可能持续增长并越过缓存预算上限,只有卸载模型或重启 Ollama 才能释放内存。优先排查 OLLAMA_KEEP_ALIVE 设

快速结论:在 Ollama 的 MLX runner 中,当使用 dense(非混合架构)模型时,进程常驻内存会随请求次数单调增长,与上下文长度或工具 schema 无关;在长会话中可能持续增长并越过缓存预算上限,只有卸载模型或重启 Ollama 才能释放内存。优先排查 OLLAMA_KEEP_ALIVE 设置为 -1(常驻)时长时间多轮工具调用场景下的内存增长趋势。

适用环境:Apple Silicon Mac(Mac Mini,48 GB 统一内存)、macOS、Ollama v0.32.12(Issue 确认时 v0.32.14 仍未包含相关修复)、dense 模型 qwen3.8:27b-mlx、MLX runner。环境变量涉及 OLLAMA_KEEP_ALIVE=10mOLLAMA_CONTEXT_LENGTH=65536OLLAMA_KV_CACHE_TYPE=q8_0OLLAMA_FLASH_ATTENTION=1OLLAMA_MAX_LOADED_MODELS=2

最快修复方案:暂无确认的一步修复方案。Issue 中已确认的临时绕过方法是完全卸载模型或重启 Ollama 进程,以释放累积的常驻内存。

注意事项:Issue 讨论中维护者最初认为 8 GB 的增长可能来自缓存快照的正常预算(maxPagedOutBytes),但用户后续补充的真实工具调用会话测试显示增长可超过该上限;freeAll() 触发时能清空 trie 但不会降低 ollama ps 的常驻 SIZE,只有重启才真正释放。该修复方案属于临时规避,不能解决根本的 trie 清理问题。

问题场景

用户在同一台 Mac Mini(Apple Silicon)上通过 /v1/chat/completions 接口连续发送请求,在 15 次互不相关、各自独立的单轮对话(无共享/增长上下文)后,观察到 ollama ps 的 SIZE 从 18 GB 稳步爬升到 23 GB;在真实的多轮工具调用会话(约 14 次 API 调用 / 15 分钟)中,内存从约 18 GB 增长到 28–30 GB。该现象在 dense 模型(qwen3.8:27b-mlx,非混合 SSM 架构)上复现,与上下文长度、工具 schema 是否存在无关。

报错原文

MLX runner: KV-cache memory accumulates per-request on dense models (independent of context size/tools), not released short of restart

原因分析

可能原因指向 cache.goclose() 的 trie 清理缺口(对应 Issue #16698 讨论中识别的根因 b):close() 在清理时没有检查该请求是否为完全 cache miss(begin()prefix == 0),导致死掉的 trie 节点被无限期保留。这解释了为什么内存增长跟请求数量(分支数量)相关,而不是跟上下文大小或工具相关。

不过 Issue 讨论中的后续测试表明:8 GB 内的增长可能是正常的缓存预算——MLX runner 中存在一个约 8 GB 的快照缓存(对应 maxPagedOutBytes),用于保存历史请求的分支点;当请求数增加时,缓存占用可上升到该上限并触发正常淘汰。但在真实工具调用场景中,内存可继续越过 26 GB 的“8 GB 预算上限”持续增长,并且即使 trie 报告 99.95% 的 cache-hit 率,常驻内存仍以约 0.3 GB/请求的速度增长,说明日志中的 matched/cached 并不代表对应内存结构真正被复用。

环境排查

  • 确认 Ollama 版本(Issue 验证时为 v0.32.12,官方 changelog 至 v0.32.14 未提及 KV-cache 内存修复)。
  • 确认模型是否为 dense 架构(如 qwen3.8:27b-mlx),以排除 #16698 中 Gated-DeltaNet 混合架构专用泄漏。
  • 使用 brew info 确认 v0.32.14 是否已安装;若已安装,先复现确认是否仍存在。
  • 检查 launchd 环境变量 OLLAMA_KEEP_ALIVE:设置为 -1(常驻)时长时间多轮工具调用更容易触发累积;设置为 10m 时经 10 分钟空闲后模型会自动卸载释放内存,但 Issue 未验证这种卸载是否会重置累积。
  • ollama ps 监控 SIZE 变化,配合 ollama.log 检查 matched/cached 统计和 failed to restore cache, freeing all caches WARN 是否出现。

解决步骤

  1. 复现确认:先通过 curl .../api/generate -d '{"model":"qwen3.8:27b-mlx","keep_alive":0}' 强制干净卸载,用 ollama ps 确认为空表后再开始测试。
  2. 基线观察(快速验证):发送 15 次互不相关的单轮请求(如 {"messages":[{"role":"user","content":"Reply with the single word OK, call number N."}],"max_tokens":5}),记录每次调用后的 ollama ps SIZE。若从 18 GB 爬升到 23 GB,且无 failed to restore cache WARN,则确认增长来自 runner 生命周期内的累积而非上下文增长。
  3. 区分正常预算 vs 泄漏:如果 SIZE 在 26 GB(18 GB 模型权重 + 8 GB 缓存预算)处停止增长并持平,则属于正常的 8 GB 快照缓存预算行为,不需要处理;如果继续增长,则为异常累积。
  4. 模拟真实工具调用场景:使用真实 system prompt + 完整工具 JSON schema(如约 89 KB 的 32-tool schema)+ 真实工具返回结果,构造 6 轮多轮工具调用会话(约 9 次 API 调用),紧接着连续发送 15 次带相同 system prompt 和 tools 的独立请求,若 SIZE 在 26 GB 后仍持续上升,可确认此问题。
  5. 临时释放方案:如需立即恢复内存,强制卸载模型并重新加载:curl http://127.0.0.1:11434/api/generate -d '{"model":"qwen3.8:27b-mlx","keep_alive":0}',然后正常加载;或直接重启 Ollama 服务。
  6. 短期规避(可优先尝试):将 OLLAMA_KEEP_ALIVE-1 改为较短间隔(如 10m),让空闲模型自动卸载;但 Issue 未确认自动卸载后重新加载会重置累积,如果内存仍持续增长,需进一步反馈到上游。

验证方法

复现原始 Issue 的测试 4:15 次互不相关的 trivial 单轮请求,观察 ollama ps SIZE 是否从 18 GB 升至 23 GB。若要验证是否为正常预算而非泄漏,继续发送至 60 次,若 SIZE 在 26 GB 处持平则说明预算内增长;若在真实工具调用场景下越过 26 GB 继续上升,则确认异常。修复后验证的关键指标:长时间工具调用会话前后,ollama ps SIZE 保持在约 18 GB 模型权重基线附近,不再随请求数逐次增长;或在触发 freeAll() 后 SIZE 实际下降(当前已验证 freeAll() 不会降低 SIZE,仅重启有效)。

参考来源

ollama/ollama #17875

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 21235

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注