系统学习 AI 学习路径:从 GPU 运行时到推理、微调与 RAG 评估

版本信息最后核对:2026-10-25

vllm-oom

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 自身配置问题

再回答四个问题:

  1. 失败发生在启动、压测还是业务高峰?启动失败偏向容量和参数,运行中失败偏向并发和请求分布。
  2. 失败瞬间 kv_cache_usage_perc 是多少?接近上限说明 KV Cache 被请求吃满;远未到上限却 OOM,要怀疑权重、碎片、其他进程或泄漏。
  3. 是所有请求都失败,还是只有长请求失败?只有长上下文失败,优先看长度上限和单请求 KV 占用。
  4. Pod 是被 GPU OOM 拖垮,还是被节点内存杀死?CUDA OOM 通常抛异常、进程不一定退出;exit 137 和 OOMKilled 指向宿主机内存。

常见原因

KV Cache 不足

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 都可能是来源。碎片的表现是“预留显存很大、实际可用块不足”,泄漏则更像已分配内存单调增长。两者都需要版本对比和长期观测,不能靠调参解决。

节点内存 OOM

GPU 显存没满,但容器被 OOMKilled,属于宿主机内存问题。常见于同时加载多个副本、张量并行拉起多个 worker、模型下载和格式转换、tokenizer 与编译缓存、页面缓存挤占,以及容器 memory limit 设置过低。退出码 137 也可能来自其他 SIGKILL 或驱逐,必须结合事件和 cgroup 计数确认。

其他显存争抢

同卡运行监控、训练任务、旧进程残留,MIG 实例规格与模型不匹配,共享 GPU 方案超卖,都会让 vLLM 看到的可用显存小于预期。启动前先用 nvidia-smi 看清整卡占用,再判断是否为 vLLM 的问题。

排查步骤

第一步:确认 OOM 类型

先看第一处 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 预算对比,就能判断是容量不足还是参数放得太开。

第三步:按启动失败处理

第四步:按运行中 OOM 处理

第五步:排查泄漏与碎片

在固定 QPS 下持续记录 GPU 显存、容器 RSS、KV 使用率和请求量,观察空闲时段是否仍上涨。对比重启前后的曲线,确认是否“重启即恢复”。再查当前 vLLM 版本的 release notes 和已知问题,逐个关闭自定义算子、插件或非默认参数做二分。碎片和泄漏的区分需要看分配器的预留量与实际使用量;如果无法从日志判断,保留现场并用最小复现提交 issue。

第六步:排查节点 OOM

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、串行化模型加载、减少同节点副本数,或把模型加载与推理拆到不同阶段。

修复与验证

按类型修复后,必须用同一套负载回归:

验证时记录 TTFT、TPOT、吞吐和 P99 延迟,确认性能没有明显退化。每次只改一个参数,保留修改前后的配置和压测结果,避免下次故障时无法回溯。

预防

相关命令

参考链接

读完自测

合上页面,先回答,再展开对照。

  1. 启动时报 “No available memory for the cache blocks” 和加载权重时 CUDA OOM,排查方向有什么不同?
  2. 把 --gpu-memory-utilization 调高,是否一定能解决 OOM?为什么?
  3. 如何区分 GPU 显存 OOM 和节点内存 OOM?
答案
  1. 前者是 KV Cache 预算不足,优先看 max-model-len、max-num-seqs 和 KV 预算;后者是权重或并行度问题,优先看模型大小、精度、量化、张量并行,以及同卡是否有其他进程。
  2. 不一定。调高会扩大 vLLM 可用的显存比例,给 KV 更多空间,但同时挤压同卡其他进程和系统余量;如果是权重过大、泄漏或节点内存不足,调高无效,甚至让争抢更严重。应结合启动日志判断。
  3. GPU OOM 通常抛出 CUDA/PyTorch 异常,进程不一定退出,显存被占满;节点 OOM 常见退出码 137、OOMKilled、dmesg 出现 Killed process,cgroup 的 oom_kill 计数增长,而 GPU 显存可能并未占满。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令