python del

引用计数法

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):
        ...

Leave a Comment