torch.cuda.empty_cache()仅回收未被引用的缓存内存,不释放正在使用的显存;应在明确有大量临时张量被删除且需分配新大张量时调用,避免在训练循环中频繁使用,多卡需遍历设备手动设置。

PyTorch显存没释放?torch.cuda.empty_cache() 该不该调用
大多数情况下,torch.cuda.empty_cache() 并不能解决你遇到的“OOM”或“显存占用越来越高”的问题——它只回收**未被任何张量引用的缓存内存**,不碰正在使用的显存。如果你看到 nvidia-smi 显示显存占用高但 torch.cuda.memory_allocated() 很低,那大概率是碎片化+缓存堆积,这时 empty_cache() 才可能有点用。
实操建议:
- 只在明确知道有大量临时大张量刚被
del或函数退出、且后续要分配新大张量时,才手动调用一次torch.cuda.empty_cache() - 不要在训练循环里每步都调用——它开销不小(毫秒级),且频繁触发反而加剧碎片
- 调用前先确认:用
torch.cuda.memory_summary()看下“allocated”和“reserved”差多少,差得多才值得清 - 注意:它对多卡默认只作用于当前
current_device,多卡需遍历torch.cuda.device_count()并torch.cuda.set_device(i)后再调用
num_workers > 0 导致显存悄悄涨满?根本原因和改法
当 DataLoader 的 num_workers > 0,每个 worker 进程会独立初始化 CUDA 上下文(即使没显存操作),导致每 worker 预占约 500MB–1GB 显存(取决于驱动/CUDA 版本)。更糟的是,这些进程里的张量若没显式转到 GPU,也可能因 pin_memory=True 在 host 端缓存 pinned memory,间接推高整体显存压力。
排查与优化:
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
立即学习“Python免费学习笔记(深入)”;
- 先关掉多进程:设
num_workers=0测试是否 OOM 消失——如果消失,基本锁定是 worker 显存污染 - 必须用多进程时,务必设
pin_memory=False(除非你真需要 host pinned memory 加速 transfer) - 避免在
Dataset.__getitem__中创建 CUDA 张量;所有 tensor 构造应在collate_fn或训练 step 内完成 - Linux 下可加
worker_init_fn=lambda x: torch.cuda.set_per_process_memory_fraction(0.2)限制单 worker 显存上限(需 PyTorch ≥ 1.11)
真正治标又治本:控制显存碎片的三个硬手段
显存碎片本质是 CUDA allocator 分配/释放不连续块后留下的空洞。PyTorch 默认用 CUDAAllocator,对小张量频繁分配极易碎。以下方法比反复 empty_cache() 有效得多:
- 统一 batch size:避免训练中动态调整
batch_size,不同尺寸张量分配模式差异大会加剧碎片 - 预分配大 buffer:对固定 shape 输入(如图像分辨率不变),用
torch.cuda.FloatTensor(N, C, H, W).zero_()提前占住一块连续显存,后续 in-place 赋值 - 启用内存复用:PyTorch ≥ 1.12 可设环境变量
CUDA_LAUNCH_BLOCKING=1+export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128(根据卡显存调),限制最大切分粒度,减少细碎块 - 极端情况:训练前执行一次
torch.cuda.reset_peak_memory_stats(),并在关键节点用torch.cuda.memory_stats()查"active_split"和"inactive_split"值,高说明已严重碎片化
为什么 del tensor 后显存还不下来?GC 和引用计数陷阱
del 只减引用计数,不等于立刻释放显存。常见陷阱包括:
- 张量被计算图保留:只要还在 autograd graph 里(比如 loss.backward() 前),就无法释放
- 闭包捕获:lambda 或内嵌函数无意中持有了 tensor 引用
- Python 循环引用:尤其在自定义
Module或Dataset中用了弱引用不当 - GPU tensor 被 CPU 变量间接引用:例如
loss.item()返回 Python float,但若之前做了loss.cpu().numpy(),numpy array 可能 hold 住原始 tensor
可靠做法:
- 用
tensor.detach().cpu().item()替代tensor.item()(避免隐式 retain graph) - 手动触发 GC:
import gc; gc.collect(); torch.cuda.empty_cache(),但仅限调试,别放训练循环 - 检查引用:用
sys.getrefcount(tensor)或weakref.getweakrefs(tensor)定位谁还拿着它
empty_cache() 和调 num_workers 不够,得结合 memory profile 和 allocator 配置动真格。

















