我有大量代码,它使用一些自定义类和许多字典。许多类都将字典添加为属性。我发现它使用了太多内存,特别是如果我正在循环 - 即使我手动删除了一些类和dicts。
我担心的是,字典会被删除,但它们包含的对象仍然存在。我需要重构代码以获得更好的内存管理,但作为一个快速的解决方案,我希望我可以递归并且积极地删除字典。如何实现这一目标?
这是一个例子......
def get_lrg():
return {1: np.zeros((1000,1000,100))}
class H():
def add_lrg(self):
fd = get_lrg()
self.lrg = fd
for cls in ['a', 'b', 'c', 'd']:
exec('{0} = H()'.format(cls) )
exec('{0}.add_lrg()'.format(cls) )
del a
del b
del c
del d
另外,在Ipython中玩这个:
fd = get_lrg()
fd2 = get_lrg()
F = {1: fd, 2: fd2}
F = {}
F = {1: fd, 2: fd2}
del F[1]
del F
并观察python应用程序的内存使用情况......即使在删除了字典“F”(例如没有引用对象)之后,它似乎也没有“释放”内存。我在我的机器上发现的结果是不可预测的。有时看起来内存会被冲洗,有时它似乎会继续使用。
答案 0 :(得分:5)
如果词典中的对象在您删除了词典之后就存在了,那么它们应该存在,因为有些代码会引用它们。
Python有两种处理内存的方式:
删除字典时,将删除对相关对象的引用。如果这是对这些对象的最后一次引用,它们将自动为您释放。
如果存在对象之间的循环,则引用计数是不够的,因为这将导致循环中的所有对象具有至少1个实时引用,即使不存在外部引用。
这就是为什么还有一个垃圾收集器清理它,虽然稍晚一点。引用计数在引用达到0时处理对象,垃圾收集器稍后开始播放。
所以不需要递归删除任何东西,只需删除对字典的引用,让Python担心其余部分。
此处还有另一个问题,提供更多细节:Why does python use both reference counting and mark-and-sweep for gc?。
您可以使用以下代码进行验证:
class X:
def __del__(this):
print("deleted")
def __init__(this):
print("constructed")
print("before")
x = X()
print("after")
del x
print("done")
这将向您显示__del__
方法作为del x
语句的一部分执行。
然后你有这个:
class X:
def __del__(this):
print("deleted")
def __init__(this):
print("constructed")
print("before")
x = X()
y = X()
x.y = y
y.x = x
print("after")
del x
del y
print("done")
这将向您显示周期(x
和y
相互引用)的处理方式不同。
然后你有了这个,我将x
存储到字典中,然后删除字典,x
对象与字典一起被删除:
class X:
def __del__(this):
print("deleted")
def __init__(this):
print("constructed")
print("before")
d = {"x": X()}
print("after")
del d
print("done")
答案 1 :(得分:3)
删除对象时,只是将引用删除到该对象。如果该对象的引用计数降为0,则将其从内存中删除,并将该对象保存到其他对象的任何引用。
例如,字典不会包含任何对象。它们包含的所有内容都是引用到其他对象。如果删除对字典的所有引用,它将自动清除,删除,并且所有引用也都会消失。引用的字典中的任何键或值都会看到它们的引用计数下降1;反过来,如果计数降到0,它们将被清理。
因此无需递归删除任何内容。如果不再引用您的对象,则会自动清除它们。
请注意,即使Python发布了对象,进程内存使用不也必须遵循。操作系统可以并且确实将内存分配给进程以减少内存流失;一个进程可能需要再次增加内存使用量,除非在其他地方迫切需要内存,否则分配会保留一段时间。