版本信息最后核对:2026-10-25
vLLM 显存与内存 OOM:区分权重、KV Cache、并发配置、泄漏和节点内存的排查路径。
vLLM 的 OOM 至少有三类表现,处理方式完全不同。
第一类是启动即失败,引擎还没开始服务就退出:
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate ...
No available memory for the cache blocks. Try increasing gpu_memory_utilization or decreasing max_model_len ...
The model's max seq len (...) is larger than the maximum number of tokens that can be stored in KV cache (...)
Engine core initialization failed
第二类是运行中失败:服务已经起来,但请求量上来、长上下文或突发流量时返回 500,日志出现 CUDA OOM,引擎可能重启;监控上能看到 num_requests_waiting 持续升高、KV Cache 使用率触顶、吞吐骤降。
第三类是进程被系统杀掉:退出码 137,Kubernetes 里显示 OOMKilled,Pod 反复重启,dmesg 出现 Out of memory: Killed process。这一类是宿主机内存或容器内存超限,不是 GPU 显存不足,排查方向完全不同。
vLLM 启动日志通常会给出显存账本,包含模型权重、非 Torch 内存、激活峰值和剩余给 KV Cache 的空间。不同版本文案略有差异,以日志中的实际数字为准。
先确认 OOM 发生在哪个阶段、哪类内存:
# GPU 显存与占用进程
nvidia-smi --query-gpu=index,memory.total,memory.used,memory.free --format=csv
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv
# vLLM 运行指标,指标名随版本变化,先宽泛匹配
curl -s http://localhost:8000/metrics \
| grep -E 'vllm:(kv_cache_usage_perc|gpu_cache_usage_perc|num_requests_running|num_requests_waiting)'
# Kubernetes 事件与上次退出原因
kubectl describe pod <pod> | grep -A8 'Last State'
kubectl get events --sort-by=.lastTimestamp | tail -20
# 宿主机内存与内核 OOM
dmesg -T | grep -i -E 'oom|killed process'
free -m
cat /sys/fs/cgroup/memory.events 2>/dev/null
第一条命令先看三件事:失败时 GPU 显存是否真的被占满、同卡还有没有别的进程、显存是缓慢上涨还是一瞬间打满。然后按线索粗分类:
| 线索 | 更可能的方向 |
|---|---|
| 启动加载权重时 CUDA OOM | 模型权重过大、精度或并行度不足 |
| 启动时报没有空间放 cache blocks | KV Cache 预算不足、max-model-len 过大 |
| 运行中偶发,KV 使用率接近上限 | 并发、输入输出长度、KV 容量 |
| 空闲时显存也缓慢上涨,重启后恢复 | 内存泄漏或分配器碎片 |
| 退出码 137、dmesg 有 OOM | 节点内存或容器内存限制 |
| 同卡存在其他进程 | 显存争抢,不是 vLLM 自身配置问题 |
再回答四个问题:
kv_cache_usage_perc 是多少?接近上限说明 KV Cache 被请求吃满;远未到上限却 OOM,要怀疑权重、碎片、其他进程或泄漏。OOMKilled 指向宿主机内存。vLLM 用 PagedAttention 管理 KV Cache,启动时会按显存预算预留 KV 空间,再按请求实际长度分块分配。每个 token 的 KV 占用由层数、KV 头数、head_dim 和数据类型决定;上下文越长、并发越高,占用越大。max-model-len 设得远高于业务需要、max-num-seqs 放得过大、客户端不限制输出长度,都会让 KV 预算快速耗尽。报错里的 “No available memory for the cache blocks” 通常就属于这一类。
权重占用约等于参数量乘以每参数字节数:bf16 约 2 字节,fp8 约 1 字节,低比特量化还要加上缩放和反量化开销。模型本身放不下、张量并行或流水并行不足、同一张卡上跑了多个副本、同卡还有其他进程,都会在加载阶段 OOM。注意权重只是账本的一部分,激活峰值、CUDA graph 和通信缓冲也要占空间。
max-num-seqs 控制同时在跑的序列数,max-num-batched-tokens 控制单次迭代的 token 上限,max-model-len 控制单请求的总长度。并发放得太开,每条序列都占一份 KV;批处理 token 放得太大,prefill 阶段激活峰值抬高;长度上限过宽,单条长请求就可能吃掉大量 KV。这些参数之间互相牵制,不能只调其中一个就期待解决所有 OOM。
长驻服务里,显存曲线在流量不变的情况下持续上涨,重启后回到低位,通常要怀疑泄漏、分配器碎片或某个版本缺陷。自定义算子、插件、NCCL 缓冲、CUDA graph 都可能是来源。碎片的表现是“预留显存很大、实际可用块不足”,泄漏则更像已分配内存单调增长。两者都需要版本对比和长期观测,不能靠调参解决。
GPU 显存没满,但容器被 OOMKilled,属于宿主机内存问题。常见于同时加载多个副本、张量并行拉起多个 worker、模型下载和格式转换、tokenizer 与编译缓存、页面缓存挤占,以及容器 memory limit 设置过低。退出码 137 也可能来自其他 SIGKILL 或驱逐,必须结合事件和 cgroup 计数确认。
同卡运行监控、训练任务、旧进程残留,MIG 实例规格与模型不匹配,共享 GPU 方案超卖,都会让 vLLM 看到的可用显存小于预期。启动前先用 nvidia-smi 看清整卡占用,再判断是否为 vLLM 的问题。
先看第一处 traceback 和最后几行日志:加载权重时失败、KV 预分配失败、生成过程中失败,还是进程直接消失。Kubernetes 环境用 kubectl logs --previous 看上一次退出日志,用 kubectl describe pod 看退出码和 Last State。GPU OOM 通常有 Python 异常;节点 OOM 往往没有完整 traceback,只有 137 和内核日志。
用启动日志里的实际数字列一张表:权重、非 Torch 内存、激活峰值、KV Cache、剩余余量。再用公式估算单 token KV 占用:
bytes/token = 2 × layers × kv_heads × head_dim × dtype_bytes
例如 7B MHA 模型、32 层、32 个 KV 头、head_dim 128、bf16 时约 0.5 MiB/token,8k 上下文单请求约 4 GiB;GQA 模型按 KV 头数比例缩小。这个公式只用于估算,最终以 vLLM 启动日志和实际压测为准。把“单请求 KV × 预期并发”与 KV 预算对比,就能判断是容量不足还是参数放得太开。
--dtype 与量化格式要匹配,否则会在加载或反量化时额外吃显存。--max-model-len 调到业务真正需要的长度,再适度下调 --max-num-seqs。如果模型权重已经占掉大部分显存,单靠调低长度可能仍然不够,需要考虑并行或量化。--gpu-memory-utilization 要看方向:调高会给 KV 更多空间,但会挤压同卡其他进程和系统余量;调低会减少 KV 预算,可能让启动直接失败。不要把它当成“越高越好”或“越低越安全”的开关,应结合启动日志小步调整并记录结果。--enforce-eager 排除 CUDA graph 的显存占用,确认它是否是压垮显存的因素;它会降低吞吐,只适合定位,不适合作为长期生产配置。--kv-cache-dtype fp8 和前缀缓存:前者降低单 token KV 占用,后者减少重复前缀的 KV 计算与占用,但都可能影响精度或带来额外开销,上线前用业务评测集和压测验证。--max-num-seqs 能减少同时驻留的序列数,但会让请求排队;下调 --max-num-batched-tokens 能降低 prefill 峰值,但可能降低吞吐。每次只动一个参数,记录 TTFT、TPOT、吞吐和错误率。max_tokens 和输入长度,拒绝超过 max-model-len 的请求,避免单条长请求把整批拖垮。在固定 QPS 下持续记录 GPU 显存、容器 RSS、KV 使用率和请求量,观察空闲时段是否仍上涨。对比重启前后的曲线,确认是否“重启即恢复”。再查当前 vLLM 版本的 release notes 和已知问题,逐个关闭自定义算子、插件或非默认参数做二分。碎片和泄漏的区分需要看分配器的预留量与实际使用量;如果无法从日志判断,保留现场并用最小复现提交 issue。
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.current
cat /sys/fs/cgroup/memory.events
ps aux --sort=-%mem | head -10
确认容器 memory limit 是否覆盖加载峰值,检查是否有多个副本同时加载模型、是否有 sidecar 或监控进程占用内存、页面缓存是否挤占。Kubernetes 里还要区分 OOMKilled、驱逐和探针失败导致的 SIGKILL。处理方式包括提高内存 request/limit、串行化模型加载、减少同节点副本数,或把模型加载与推理拆到不同阶段。
按类型修复后,必须用同一套负载回归:
memory.events 中 oom_kill 不再增长,Pod 退出码不再出现 137,节点内存有余量。验证时记录 TTFT、TPOT、吞吐和 P99 延迟,确认性能没有明显退化。每次只改一个参数,保留修改前后的配置和压测结果,避免下次故障时无法回溯。
max_tokens,为请求设置超时,避免单条请求无限占用资源。合上页面,先回答,再展开对照。
--gpu-memory-utilization 调高,是否一定能解决 OOM?为什么?max-model-len、max-num-seqs 和 KV 预算;后者是权重或并行度问题,优先看模型大小、精度、量化、张量并行,以及同卡是否有其他进程。OOMKilled、dmesg 出现 Killed process,cgroup 的 oom_kill 计数增长,而 GPU 显存可能并未占满。
小程序版
微信扫码,随时查命令