ComfyUI 在无 swap 分区的 Linux 上 pin 太多内存:v0.31.0 之前怎么判定与规避
云上开的 Linux 机器,很多镜像默认压根不划 swap 分区。平时跑 Web 服务无所谓,但 ComfyUI 从 v0.30.0(2026-08-03)起把「pinning 基础设施 + MRU 策略把权重加载到进程 RAM」(PR #15027)搬进了主线,这台没有 swap 的机器就开始跟一个你从没配置过的常量打交道。
到 v0.31.0(2026-08-08),release notes 里明确写了一条:Linux 无 swap 分区时不再 pin 太多内存(PR #15266)。也就是说,这是官方在 2026-08-08 才修掉的问题——早于这个版本的用户会撞上,晚于这个版本的用户不需要做任何事。这篇就把「我到底是不是这一类」讲清楚。
现象长什么样
典型画像是:一台没有 swap 分区的 Linux 机器,内存看起来还挺富裕,但 ComfyUI 跑起来之后系统整体的内存压力明显偏紧;启动日志里 Enabled pinned memory 那一行给出的上限,比你按「总得给系统留点余量」的直觉预期要大。我们核对到的源码文案里,没有一条是「pinned memory 超限」这种直白报错,所以这件事很容易被当成别的问题去查。
需要先摆正一件事:我们没有在本机复现过这个场景,下面所有判定动作都是照着 comfy/model_management.py(v0.31.0)里的常量语义和源码里的原始日志文案来的。别指望我给你一个「内存涨到多少 MB 就是它」的阈值,源码里没有这种东西。
上限是怎么算出来的:两套公式
pinned memory 的上限常量初始值是 MAX_PINNED_MEMORY = -1,小于等于 0 时视为不启用。之后按操作系统分成两条完全不同的路:
Windows:
MAX_PINNED_MEMORY = ram * 0.40 # Windows limit is apparently 50%
其它系统(含 Linux):
MAX_PINNED_MEMORY = max(ram * 0.40, min(ram * 0.90, ram - 4 * 1024**3, ram + get_disk_swap_total() - 16 * 1024**3))
第一条一目了然:Windows 只按物理内存的 40% 取值,注释说系统上限「apparently 50%」,代码自己往下让了一档。Windows 这条公式里没有任何 swap 项——这一点后面判定时要用到。
第二条值得慢慢读,它是本篇的核心。里层是一个 min,三个候选:RAM 的 90%、RAM - 4GB、RAM + swap 总量 - 16GB。取最小,等于「三道余量里最紧的那道说了算」。外层再用一个 max 跟 ram * 0.40 比一次,等于给了一个不会再低的地板。
把这三项拆开看谁在什么时候起作用(下面是纯算术推演,不是实测数据):
RAM - 4GB和RAM * 0.90谁小,取决于内存总量。内存小于 40GB 时,RAM - 4GB这一项更紧;内存更大时,90% 那一项才开始咬合。也就是说,中小内存机器上真正生效的是「留 4GB 给别人」。RAM + swap - 16GB这一项,swap 越大它越松。swap 总量超过 12GB 之后,它就基本不再是最紧的那个。- 而 swap 为 0 时,这一项直接退化成
RAM - 16GB——这才是「无 swap 分区」这件事在公式里的落点。
再套一下外层的 max,拿两台零 swap 的 Linux 机器手推一遍(以下都是拿公式做的算术,不是任何机器上的实际读数):
| RAM 总量 | ram * 0.40 | 里层 min 三项 | 里层 min 结果 | 最终上限 | 谁说了算 |
|---|---|---|---|---|---|
| 32GB | 12.8GB | 28.8 / 28 / 16 | 16GB | 16GB | 里层的 RAM - 16GB |
| 16GB | 6.4GB | 14.4 / 12 / 0 | 0 | 6.4GB | 外层的 40% 地板 |
第二行是这段公式里最反直觉的地方:16GB 内存又没有 swap 时,RAM + swap - 16GB 这一项直接算成 0,里层 min 因此归零;但外层的 max 又把 40% 这块「地板」抬了回来,最终仍然会 pin 到 6.4GB。换句话说,「里层被 swap 压到很低」和「实际生效的上限很低」不是一回事——只要 RAM 不到 26.67GB(即 RAM - 16GB 低于 RAM * 0.40 的那条分界),零 swap 机器上真正生效的其实就是那条 40% 地板,跟有没有 swap 已经无关了。
这个地板设计是有意的(否则小内存机器上 pinned memory 会被压到没意义),但它同时意味着:「没 swap」这件事在公式里的影响并不是「一路压到 0」,而是在特定内存区间里被公式的另一端接住。判断自己这台机器落在哪一行,比记住「没 swap 就会出事」有用得多。
顺带说一句:这个公式是 2026-08-09 核对 master 时的样子,已经包含了 PR #15266 之后的状态。PR #15266 之前的公式长什么样,我们没有依据,所以不写。 你只需要记住 release note 的那句话:v0.31.0 起,无 swap 的 Linux 不会再 pin 那么多。
怎么确认自己撞的是这个问题
四个动作,从快到慢:
第一步,确认版本。 如果你已经在 v0.31.0 或更高,PR #15266 已经在里面了,这条线索到此为止,去查别的。ComfyUI 大约每两周发一个 major stable 版本,README 还特别提醒过:stable release tag 之外的 commit 可能非常不稳定、会弄坏很多自定义节点——所以别为了这一个修复去跟 master。
第二步,确认机器真的没有 swap。 free -h 或 swapon --show 都能看(这两条是通用 Linux 命令,不是 ComfyUI 文档里的内容)。swap 那一行是 0,才谈得上后面的事。
第三步,看启动日志里的这几行。 源码里的原始格式串是:
Total VRAM {:0.0f} MB, total RAM {:0.0f} MB
Set vram state to: {vram_state.name}
Enabled pinned memory {}
Enabled pinned memory 后面跟的是 MB 数,按这条日志的语义,它是「启用了 pinned memory」时才打印的,跟的就是上面那个公式算出来的上限。所以这一行没出现,就先别按 pin 过多这条线查——常量停在 -1 那种「视为不启用」的状态时,公式算多少都不构成问题。反过来,这一行的数值和你按公式手算的能不能对上,是这篇里最直接的一条判据:对得上,说明你读的就是本篇讲的这套逻辑;对不上,说明你的版本或参数跟这里的前提不一样,先回去核版本。
第四步,在日志里搜这两条告警。 源码里有两条与 swap 直接相关的文案:
Could not get amount of swap memory on system.
Could not read Windows swap usage; falling back to RAM-pressure pin eviction: %s
这两条本身说明「拿不到 swap 信息」是官方预期之内的情况,程序会退回按 RAM 压力做 pin 淘汰,而不是崩掉。第一条出现在通用路径上,第二条从文案就能看出是 Windows 侧的。看到它们不等于出了故障,但它们能告诉你「swap 这条信息在你这台机器上是没拿到的」——配合第二步的 swapon --show,就能把「没有 swap」和「有 swap 但读不到」区分开。
处置:升级优先,老版本才谈规避
首选是升到 v0.31.0(2026-08-08)。PR #15266 就是冲着这个场景来的,没有比这更直接的处置。
如果你因为自定义节点兼容性等原因暂时钉在旧版本上,兜底开关是 --disable-pinned-memory。下面几个参数的 help 语义都以 comfy/cli_args.py(v0.31.0)为准,参数与默认值会随版本变动,用之前先跑一次 --help 对一下。--disable-pinned-memory 的 help 说明就是「禁用 pinned memory」,整条关掉,公式算多少都不再有意义。代价也很直白:v0.30.0 引进的那套 pinning 加载路径你就用不上了。
比整条关掉更细的一把刀是 --cache-ram。它是默认的缓存模式,接受最多两个值:第一个设 active-cache 阈值,可选的第二个设 inactive-cache / pin 阈值。不给值时的默认是 active 取系统 RAM 的 10%(最小 2GB、最大 10GB),inactive 取系统 RAM 的 100%(最大 128GB)。想控制 pin 的量而不是彻底禁用,动第二个值。
另一个相关开关是 --fast-disk:help 写的是相比未 pin 的 RAM 优先用磁盘做动态加载与卸载,对拥有快速 NVME 磁盘的用户可能更快。它和 v0.23.0(2026-06-01)的「offload 到磁盘」是配套的。它不是这个问题的解药,只是在你决定少 pin 一点之后,给权重换一条去处。
一条组合示例:
python main.py --disable-pinned-memory --cache-ram 8
或者保留 pinning、只压 pin 阈值:
python main.py --cache-ram 8 24 --fast-disk
以上为按官方参数语义组合的示例,未逐项实测,以官方文档与 python main.py --help 的实际输出为准。顺便说,跑一次 --help 也是判断版本的土办法:参数在不在、help 文案是什么措辞,比你回忆版本号可靠。
至于「那我给机器加个 swap 分区行不行」——从公式看,get_disk_swap_total() 确实是加分项,swap 上去了那一项就松了。但官方 release note 只说了「不再 pin 太多」,没有把加 swap 写成推荐做法,加不加是你的运维决策,不是 ComfyUI 文档的建议。
处置后怎么验证
重启 ComfyUI,还是看那三行日志:
Enabled pinned memory {}这一行的 MB 数应该明显变小;用了--disable-pinned-memory的话,按 help 语义它应该不再出现。Total VRAM ... total RAM ...那一行不受影响,它只是报告总量,可以拿来核对你手算公式时用的 RAM 数对不对。Set vram state to: ...这一行打印的是 VRAM 状态机的枚举名(DISABLED/NO_VRAM/LOW_VRAM/NORMAL_VRAM/HIGH_VRAM/SHARED)。改 pinned memory 相关参数不应该改变这一行;如果它变了,说明你顺手动了别的东西,回去看命令。
验证的重点是日志对不对,而不是「感觉快了」。这个改动影响的是进程 RAM 的占用策略,别拿出图快慢来判断有没有生效。
什么情况说明不是这个原因
这一节别跳过,不然很容易在一条错路上耗一下午。
你在 Windows 上。 Windows 分支的公式是干干净净的 ram * 0.40,里面没有 swap 项。Windows 的页面文件是另一码事,源码里跟它相关的只有那条 Could not read Windows swap usage; falling back to RAM-pressure pin eviction 的 fallback 告警。所以「无 swap 分区」这个说法在 Windows 上根本不成立。
日志里没有 Enabled pinned memory 这一行。 pinned memory 没启用,上限公式算什么都无所谓。
你已经在 v0.31.0 或更高。 PR #15266 已经带上了。
加了 --disable-pinned-memory 之后现象一点没变。 这基本能排除 pinned memory 这条线。往这几个方向转:
- 官方仓库 issue #15255 反映了「Dynamic VRAM streaming crashes all generations with
HostBuffer.read_file_slice failed→ CUDA OOM」这一现象,标题标注为 Aug 3 2026 更新后的 regression,创建于 2026-08-03,标签为 Bug,截至 2026-08-09 仍为 open。时间点正好压在 v0.30.0 那次 pinning 改动上,如果你的现象是崩在生成阶段而不是内存占用偏高,先看看是不是这一类。 - 官方仓库 issue #14340 反映了「VRAM OOM on Linux with large singular allocation but should be within limits」这一现象,创建于 2026-06-08,标签为 Potential Bug,截至 2026-08-09 仍为 open。
- 官方仓库 issue #13730 反映了「LTX 2.3 FP8/Q4KM 在 RX 7900 XTX + ROCm 上会在
Requested to load LTXAV处停住,除非把 dynamic VRAM / pinned memory / async offload 关掉」这一现象,创建于 2026-05-06,标签为 Potential Bug,截至 2026-08-09 仍为 open。
这三条都得加个前提。后两条挂的 Potential Bug 标签,字面意思是「疑似 bug」,不是官方已确认;第一条挂的是 Bug,但同样只说明它被这么打了标,不等于根因已经查清。更要紧的是:我们只看了这几个 issue 的标题、创建日期和标签,没有读过正文与评论,所以里面有没有可用的解法、根因是什么、有没有维护者回复,这篇一个字都不敢替它说。它们在这里的唯一作用,是给你一个「还能往哪个方向找」的线头。
日志里出现内存泄漏提示。 源码里的原文是 Potential memory leak detected with model {类名}, doing a full garbage collect, for maximum performance avoid circular references in the model code.,更严重时是 WARNING, memory leak with model {类名}. Please make sure it is not being referenced from somewhere. 官方文案给的方向是「避免循环引用 / 检查谁还在引用它」,这几乎总是指向自定义节点持有了模型引用。这时候该做的是 --disable-all-custom-nodes 起一次,做二分排查,跟 swap 和 pin 没关系。
判定这件事的成本很低——看一眼版本号、swapon --show 一行、日志里搜两个关键词,五分钟内就能给出「是」或「不是」。真正费时间的是没做这五分钟,直接开始调参数。
延伸阅读
本文依据 ComfyUI 官方仓库(github.com/Comfy-Org/ComfyUI)的 README、comfy/cli_args.py、
release notes 与官方安全公告整理,核对日 2026-08-09,对应版本 v0.31.0;
文中引用的 issue 状态为该日期的快照。本文内容为官方文档与源码口径,非本机实测。
参数、默认值与功能随版本变动,请以官方文档与 python main.py --help 的实际输出为准。