引用计数法
python使用的是“按引用计数”的方法作为垃圾回收的依据
即:若堆中某内存块不再被其他地方引用,则会对该区域进行垃圾回收
del的作用就是删除引用(拿java做比较就是:仅删除栈中的变量,但其指向的堆中的对象依然存在。通过计算该对象当前被引用次数判断其内存是否要被回收)
注:del只是删除引用,和c++中的free 以及java中的手动置null都不一样
import sys
# getrefcount() 方法获取对象引用次数,id() 方法获取对象内存地址
# 为数字1开辟一块内存(地址为140703783919360),并创建一个num引用指向它
num = 1
sys.getrefcount(num) # 121
print(id(num)) # 140703783919360
# 因为python是按引用传递(传地址),故这里创建了一个num_r1引用并指向数字1所在的地址
num_r1 = num
sys.getrefcount(num) # 122
print(id(num_r1)) # 140703783919360
# 删除一个引用后,数字1地址的引用减少1
del num_r1
sys.getrefcount(num) # 121
# 对数组来说,数组元素仅仅是元素对象的引用
arr = [1,2,3]
sys.getrefcount(arr[0]) # 122
i0 = arr[0]
sys.getrefcount(arr[0]) # 123
del arr[0]
print(f'arr: {arr}, arr[0]: {arr[0]}') # arr: [2, 3], arr[0]: 2
sys.getrefcount(i0) # 122
pytorch
在使用pytorch训练模型时,会发现随着训练时间的增加,使用GPU的显存也逐步增加,这有可能是因为显存没有及时被回收进而导致了内存泄漏,进一步会导致显存溢出进而训练终止。
一个解决办法是在次前馈后都手动释放内存(个人觉得这样做用处不大,但我看到网上也有人说有用,我自己没试过)
注:pytorch有实现自己的显存释放机制,pytorch和显卡真实显存的关系就像java中的 JMM 和实际内存的关系。pytorch会一次申请大块显存,然后自己掌控对该显存的控制。故查看显卡的显存占用并不能真实地反映pytorch中真实的显存占用,事实上有一部分是pytorch自己预留的,而 torch.cuda.empty_cache() 的作用就是归还这一部分显存
# 显式删除引用
del x, y, pred, loss
# 显式释放预留显存
torch.cuda.empty_cache()
另外,在测试模型的时候,关闭梯度计算以节约显存(不关的话占用也不会累积,只是会增加单次计算的显存消耗)
with torch.no_grad():
for idx, (x, y) in enumerate(dataloader):
...