pytorch视频帧处理中内存不释放的根源是cuda缓存机制和计算图残留,必须在每帧处理后立即调用torch.cuda.empty_cache(),配合model.eval()和torch.no_grad()禁用梯度,del仅解除引用而empty_cache才真正释放显存。

PyTorch视频帧处理中内存不释放的典型表现
加载一帧就 torch.tensor() 一次,循环几十帧后 OOM;用 cv2.VideoCapture 读帧 + torch.from_numpy() 转换后没显式释放,nvidia-smi 显示 GPU 内存持续上涨;即使函数退出、变量被重赋值,torch.cuda.memory_allocated() 仍不回落。
根本原因不是“没删变量”,而是 PyTorch 的 CUDA 缓存机制默认复用显存块,且 autograd 计算图残留(哪怕没 .backward())也可能持引用。单纯靠 Python 垃圾回收器(gc.collect())无法触发 CUDA 显存释放。
必须显式调用 torch.cuda.empty_cache()
这不是可选项,是视频批量帧处理的强制操作点:
-
torch.cuda.empty_cache()不会清空模型参数或当前活跃 tensor,只释放未被任何 tensor 引用的缓存显存 - 它应在每帧处理完、且确定后续不再访问该帧对应 tensor 后立即调用(尤其在
with torch.no_grad():块内做完推理后) - 不要等到循环结束才调用——缓存会在多帧间不断累积
for i in range(frame_count):
ret, frame = cap.read()
if not ret: break
tensor = torch.from_numpy(frame).permute(2, 0, 1).float().unsqueeze(0).to('cuda')
out = model(tensor)
del tensor, out # 先解引用
torch.cuda.empty_cache() # 立即释放缓存
注意:del 本身不释放显存,只是移除 Python 引用;empty_cache() 才真正归还给 CUDA 驱动。
避免隐式计算图构建导致的 Tensor 持有
只要 tensor 的 requires_grad=True(或由它派生),PyTorch 就会构建计算图并持有输入 tensor 引用,即使你没调 .backward()。视频处理中绝大多数场景不需要梯度:
- 用
model.eval()+torch.no_grad()双保险,禁止 grad 开关和计算图追踪 - 输入 tensor 显式设为
requires_grad=False(虽然no_grad下默认如此,但加一层更稳) - 切勿在推理循环里写类似
loss = criterion(pred, target); loss.backward()—— 这是训练逻辑,混入推理会快速拖垮内存
gc.collect() 在 CUDA 场景下作用有限但仍有用处
gc.collect() 对 CPU 内存中的 tensor 对象(如 numpy array、python list of tensors)回收有效,但它完全不影响 CUDA 显存分配状态:
- 如果你用
list.append(tensor)积累帧数据,忘了清空 list,gc.collect()可帮回收 list 本身和其中的 CPU-side tensor 元信息 - 但如果 tensor 已转移到 GPU,
gc.collect()不会让torch.cuda.memory_allocated()下降 - 实操建议:仅在确认有大量 CPU 端中间对象(如预处理后的 PIL.Image、临时 dict)堆积时,配合
del使用;别指望它解决显存泄漏
真正容易被忽略的是:PyTorch 的 CUDA 缓存行为在不同版本中略有差异,1.12+ 默认启用 torch.cuda.memory_stats() 中的“allocated vs reserved”分离机制,所以看 memory_allocated() 低不代表显存真空闲——得同时监控 memory_reserved(),并在关键节点调 empty_cache() 才算闭环。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











