
1. CodeX CLI 长会话内存溢出与 OOM killed 的真实场景CodeX CLI 内存溢出memory leak / High RAM usage / OOM killed这件事我最早是在一个 3 万多行的 TypeScript 单体仓库里撞上的。当时用codex 重构整个项目跑一个跨模块任务跑到第 7 轮对话时终端直接甩出Killed: 9活动监视器里 codex 进程的 RSS 已经顶到 2.3GB。后来换成codex 分析大型项目也一样Node.js 堆先报JavaScript heap out of memory紧接着系统 OOM killer 把进程干掉。这个问题的本质是CodeX CLI 是 Node.js 写的默认老生代堆上限约 1.5GB64 位系统而长会话会把每一轮对话历史、工具调用输出、被读取的文件内容全部挂在内存里不释放。一旦你让它分析大目录、连续追问十几轮、或者工具返回了几万行日志内存曲线就是一条只涨不跌的斜线最后撞上堆上限或系统物理内存被 OOM killed。它适合谁排查三类人最该看一是本地跑 CodeX CLI 做长任务重构的开发者二是在 Docker 容器里跑 codex 被--memory限制掐死的 CI/Agent 场景三是把 CodeX CLI 接到统一 API 通道后想确认「内存回落」到底是模型侧问题还是本地进程问题的同学。这篇我会从config.toml骨架讲起演示怎么通过 TaoToken 统一 Key/API 通道接入把复现、定位、验证内存回落这条链路走完让你能按步骤完成一次可观测的修复确认。先给结论方向内存溢出 35% 来自大项目一次性加载、25% 来自长对话历史累积、20% 来自 Node 默认堆限制剩下是工具输出缓存、Docker 限制和真正的内存泄漏。排查顺序应该是「先加堆 → 再减占用 → 再清历史 → 最后看是不是真泄漏」。2. TaoToken 统一 Key 通道前置准备与 config.toml 骨架在动手排查内存之前先把接入层固定下来否则你很难判断 OOM 是本地进程问题还是请求链路问题。我用 TaoToken 做统一 Key/API 通道的原因很直接CodeX CLI 支持自定义 Base URL 和 API Key把模型请求收敛到一个入口后日志、模型 ID、超时都能统一观察排查内存时变量更少。TaoToken 官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个不加 UTM。你需要先去控制台拿 Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 然后在 API Keys 页面创建 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。CodeX CLI 的配置走~/.codex/config.toml这是它的主配置文件。下面是我实测可用的骨架注意model_provider和model_providers两段要对应上# ~/.codex/config.toml model gpt-5-codex model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api responses [history] persistence none [profiles.lowmem] model gpt-5-codex model_provider taotoken几个关键点解释一下。base_url指向 TaoToken 的 API 基址env_key表示 Key 从环境变量TAOTOKEN_API_KEY读取不要把 Key 明文写进 toml。wire_api responses是 CodeX CLI 新版走 Responses API 的写法如果你用的是老版本可能要用chat这个后面排障会讲。[history] persistence none是我为了排查内存特意关掉历史落盘的配置能减少一部分磁盘和内存开销。环境变量这样设export TAOTOKEN_API_KEYsk-你的TaoTokenKey export NODE_OPTIONS--max-old-space-size4096NODE_OPTIONS这一行是内存排查的核心开关先把堆从默认 1.5GB 提到 4GB能立刻区分「是堆太小」还是「是真泄漏」。如果你要长期生效写进~/.zshrc或~/.bashrcecho export TAOTOKEN_API_KEYsk-你的TaoTokenKey ~/.zshrc echo export NODE_OPTIONS--max-old-space-size4096 ~/.zshrc source ~/.zshrc如果你用的是 Claude Code 想接同一套通道配置在~/.claude/settings.jsonBase URL 同样是https://taotoken.net/apiKey 用同一个Model ID 按文档填。Cline 的 MCP 配置则在cline_mcp_settings.json里三件套Base URL Key Model ID缺一不可。Codex 的auth.json如果你走的是 OAuth 模式路径在~/.codex/auth.json但用 TaoToken 的 env_key 方式就不需要它。前置准备做完你应该有一个可用的 TaoToken Key、一份config.toml、NODE_OPTIONS已设。接下来进入可复制配置和复现步骤。3. 可复制配置config.toml 骨架与内存复现步骤这一节给你能直接抄的配置和复现脚本。先说config.toml的完整版包含低内存 profile 和日志观察点# ~/.codex/config.toml model gpt-5-codex model_provider taotoken approval_policy on-request [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api responses request_max_retries 2 stream_max_retries 2 [history] persistence none [profiles.lowmem] model gpt-5-codex model_provider taotoken [tui] notifications falserequest_max_retries和stream_max_retries调小是为了避免重试时把请求体反复堆在内存里长会话下这个细节能省不少。[tui] notifications false关掉通知减少 TUI 渲染开销。复现内存增长我用的方法是开一个监控窗口 一个 codex 窗口。监控窗口跑while true; do ps aux | grep -E codex | grep -v grep | awk {print $2, $4%, $6/1024MB, $11} sleep 2 done$6是 RSS实际物理内存单位 KB除以 1024 得到 MB。你会看到 codex 进程的 RSS 随对话轮数上涨。然后 codex 窗口里跑一个会累积历史的场景codex 读取 src 目录下所有 ts 文件逐个分析潜在的空指针风险这一步会触发大量文件读取和工具输出缓存是内存涨得最快的情况。观察监控窗口如果 RSS 在 30 秒内从 300MB 冲到 1.5GB 以上说明堆限制在起作用如果设了--max-old-space-size4096后能涨到 3GB 才被杀那基本确认是「占用型」而非「泄漏型」。分块处理是降低占用的直接手段对比一下# 错误示范一次性全量 codex 分析所有文件 # 正确示范分目录 codex 分析 src/auth/ 目录 codex 分析 src/api/ 目录 codex 分析 src/utils/ 目录--print模式执行后退出不保持会话状态内存会随进程结束释放codex --print 分析 src/index.js 的依赖树 --max-turns 5--max-turns 5限制工具调用轮数--max-tokens 2000限制输出长度这两个参数配合能把单次任务的内存峰值压下来。Docker 场景下docker run --memory4g --memory-swap8g \ -e NODE_OPTIONS--max-old-space-size4096 \ -e TAOTOKEN_API_KEYsk-你的TaoTokenKey \ -v ~/.codex:/root/.codex \ codex-env codex 分析 src/auth/--memory4g是容器硬上限--memory-swap8g允许用 4G 交换NODE_OPTIONS要和容器内存匹配别设成 8192 却只给 4G 容器内存那样堆还没满容器先被 OOM。4. 验证请求与内存回落成功结果怎么看配置和复现都做完这一节讲怎么验证「内存回落」和「请求成功」。先验证 TaoToken 通道是通的用模型对话页面确认 Key 有效 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。在页面里发一条消息能正常返回就说明 Key 和 Base URL 没问题。回到 CLI跑一个最小请求验证链路codex --print 回复 OK --max-turns 1预期输出是模型返回OK进程正常退出。如果这一步就报 401说明 Key 或 env_key 名字对不上去 §5 排障。验证内存回落我用的对照实验是这样的。先跑一个长会话任务记录峰值 RSScodex 分析 src 目录列出所有 TODO 注释监控窗口记下峰值比如 1.8GB。然后退出用--print模式跑同样的任务codex --print 分析 src 目录列出所有 TODO 注释 --max-turns 3再记峰值通常会降到 600MB–900MB 区间因为进程执行完就退出历史不累积。这就是「内存回落」的可观测证据。如果你要更精确地看 Node 堆可以在启动时加--trace-gcNODE_OPTIONS--max-old-space-size4096 --trace-gc codex --print 分析 src/auth/ --max-turns 3输出里会打印每次 GC 前后的堆大小你能看到Mark-Compact之后堆有没有降下来。如果 GC 后堆仍然持续上涨那才是真泄漏如果 GC 后能回落说明只是占用高加堆 分块就能解决。成功结果的判断标准我列一下一是codex --print能正常返回并退出退出码 0二是监控窗口里进程结束后 RSS 归零三是--trace-gc输出里Mark-Compact后堆有下降四是长会话场景下峰值 RSS 不超过你设的--max-old-space-size的 80%。四条都满足这次修复确认就算完成。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth排查内存时最容易混进来的是接入层报错这些报错和 OOM 长得像但根因完全不同。我按真实遇到的顺序列。401 Unauthorized。报错长这样Error: 401 Unauthorized {error:{message:Invalid API key}}根因是TAOTOKEN_API_KEY没设、设错或者config.toml里env_key写的名字和环境变量不一致。检查echo $TAOTOKEN_API_KEY grep env_key ~/.codex/config.toml两个名字必须完全一致。注意别把 Key 写进 toml 的api_key字段CodeX CLI 优先读env_key指定的环境变量。local proxy failed。报错Error: local proxy failed: connection refused这个通常是base_url写错或网络不通。确认base_url https://taotoken.net/api注意结尾不要多加/v1CodeX CLI 会自己拼路径。用 curl 直接验证curl -s -o /dev/null -w %{http_code} https://taotoken.net/api返回 200 或 401 都说明域名可达返回 000 是网络问题。reading choices 报错。报错Error: reading choices of undefined这是wire_api配错导致的。CodeX CLI 新版默认走 Responses API如果你配了wire_api chat但服务端返回的是 Responses 格式解析choices就会 undefined。改成wire_api responses即可。反过来如果你的 CodeX CLI 版本较老只支持 chat就改成chat。版本查看codex --versionOAuth 相关报错。报错Error: OAuth token expired如果你之前用 OAuth 登录过~/.codex/auth.json里可能残留旧 token和 env_key 方式冲突。直接删掉或改名mv ~/.codex/auth.json ~/.codex/auth.json.bak然后重新用TAOTOKEN_API_KEY启动。注意auth.json和config.toml的env_key是两套认证方式别同时用。OOM killed 但堆没满。报错Killed: 9如果--trace-gc显示堆才 1GB 就被杀那不是 Node 堆问题是系统物理内存或容器限制。检查free -h # Linux vm_stat # macOS docker stats # 容器这种情况要么加 swap要么降--memory之外的并发要么把--max-old-space-size调到比容器内存略小。内存持续增长不回落。如果 GC 后堆仍涨可能是真泄漏。先升级 CodeX CLI 到最新版npm i -g openai/codexlatest然后定期重启长任务拆成多个--print调用。如果升级后仍泄漏用--trace-gc日志提 issue。6. 长期编码与 Agent 场景的通道选择排查完内存如果你打算长期用 CodeX CLI 做编码或跑 Agent接入层的稳定性比单次修复更重要。我现在的做法是把模型请求统一走 TaoTokenCodeX CLI、Claude Code、Cline 共用一套 Key这样内存排查时只需要盯本地进程不用怀疑多个 Key 的差异。长期编码场景建议用 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合高频、长会话的 Agent 调用。Claude Code 接入的话配置在~/.claude/settings.jsonBase URL 用https://taotoken.net/apiModel ID 按文档填Key 复用同一个。Claude Code 的 Anthropic 兼容入口文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 。我自己的长期配置是这样的NODE_OPTIONS固定 4096config.toml里history.persistence none长任务一律--print拆块每完成一个模块就退出重开。这套组合跑了一个月再没出现过 OOM killed。内存回落这件事本质不是「修好了一个 bug」而是把「长会话累积」这个模式换成了「短会话 分块」从根上避开了堆上限。最后留一个我常用的监控一行命令你可以直接贴进 shellwatch -n 2 ps aux | grep codex | grep -v grep | awk {print \$2, \$4\%\, \$6/1024\MB\}内存涨到--max-old-space-size的 80% 就手动退出重开比等 OOM killer 动手体面得多。