# 性能与内存 > 本文档由 README 拆分而来:实测内存占用、调优手段、thinking 预算。 > 返回 [README](../README.md)。 ## 内存占用(实测与调优) 内存不是一个定数:它随**配置的源数量**增长(每个源一个 `http.Transport` 与连接池、 一组健康状态),与运行时长无关。本机实测(Linux x86_64,12 核): | 部署形态 | 启动 RSS | 稳态 RSS | |---|---|---| | 1 源 / 1 适配器(最小配置) | ~4 MB | ~10 MB | | 1 源 + 29 MB 历史审计日志 | ~19 MB | ~20 MB | | **16 源 / 13 适配器 / 59 模型(本机生产)** | ~28 MB | **~32–35 MB** | > 历史参考:本项优化前同一生产配置为 **~105 MB**。降幅来自三处:审计日志的**原始记录** > 不再常驻内存(约 25 MB;统计聚合仍扫全量,但扫完即释放)、Lua 状态池不再单调增长、 > 以及下面两个运行时开关。 内存构成(生产实例分段测量,`/proc//smaps`): | 区域 | RSS | 说明 | |---|---|---| | Go 堆 | ~14 MB | provider/registry/scheduler 结构 + 连接池缓冲 | | 其他匹名(线程栈 / LuaJIT chunk / runtime) | ~12 MB | 与线程数、已加载适配器数相关 | | 二进制 text+rodata | ~8 MB | 映射的可执行文件页(只读、可被内核回收) | | Go runtime 预留 | ~4 MB | VSZ 上看到的 2 GB+ 是地址空间预留,不占物理内存 | | 共享库 | ~3 MB | libc / libluajit / libm | **两个推荐的部署开关**(只适用于环境变量,无需改代码): ```ini # /etc/systemd/system/modelrouter.service Environment=GOGC=50 Environment=MALLOC_ARENA_MAX=2 ``` - `MALLOC_ARENA_MAX=2`:LuaJIT 的分配走 cgo → glibc malloc,glibc 默认允许 `8×nproc` 个 per-thread arena,每个碰到 malloc 的 OS 线程会占用一个(各约 1 MB,且**不归还给系统**)。 实测从 8–15 个 arena(约 7–12 MB)降到 **0**。 - `GOGC=50`:把 Go 堆增长目标减半。**单独使用无效**(省下的堆会立即被更多 glibc arena 吃掉,实测 20.3 → 21.5 MB 反而变大),必须与 `MALLOC_ARENA_MAX` 配合, 两者同时开启才降 **~19%**。网关是 I/O 密集型(本机 9 小时仅消耗 1min10s CPU), 多出的 GC 周期与它的空闲 CPU 相比可忽略。 > 桌面版(Electron)已在 `cmd/gui/main.js` 里默认为内嵌核心注入这两个开关;手动 export 同名 > 环境变量可覆盖。如果你用自己的 systemd unit / 容器,建议照上面加上。 可选:`GOMEMLIMIT=48MiB` 作软上限,实测再省 ~1 MB;代价是逐近上限时 GC 转激进, 温和场景不必开。 ## thinking 预算 请求体带 `"thinking": {"enabled": false}` 时关闭推理;`"thinking": {"enabled": true, "budget_tokens": 4096}` 指定预算。内核只定义这个**中立**语义字段,映射到具体协议由 适配器负责(Anthropic `thinking.budget_tokens`、Gemini `thinkingConfig.thinkingBudget`、 DeepSeek `thinking.type = "disabled"`)。内核不再向适配器泄漏 `disable_thinking` / `extra_body` 这类私有字段。