0

我有大量代码,其中使用了一些自定义类和很多字典。许多类都将字典添加为属性。我发现它使用了太多的内存,尤其是当我在循环时——即使我手动删除了一些类和字典。

我担心正在发生的是字典被删除,​​但它们包含的对象仍然存在。我需要重构代码以获得更好的内存管理,但作为一个快速的解决方案,我希望我可以递归地、积极地删除字典。这将如何实现?

这是一个例子......

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”已被删除(例如,没有对对象的引用)之后,它似乎也不会“释放”内存。我在我的机器上发现结果是不可预测的。有时它确实似乎内存被刷新,其他时候它似乎一直在使用。

4

2 回答 2

6

如果字典中的对象在您摆脱字典后仍然存在,那么它们应该存在,因为某些代码引用了它们。

Python 处理内存有两种方式:

  1. 引用计数
  2. 标记和清除垃圾收集器

当您删除字典时,您正在删除对相关对象的引用。如果这是对这些对象的最后引用,它们将自动为您释放。

但是,如果对象之间存在循环,则引用计数是不够的,因为这将导致循环中的所有对象都具有至少 1 个活动引用,即使不存在外部引用也是如此。

这就是为什么还有一个垃圾收集器可以清理它的原因,尽管时间稍晚。当引用达到 0 时,引用计数会处理对象,垃圾收集器稍后会发挥作用。

所以不需要递归删除任何东西,只需删除对字典的引用,剩下的就让 Python 操心。

这里还有一个关于 SO 的问题提供了更多细节:为什么 python 对 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")

这将向您展示循环(两者xy相互引用)的处理方式不同。

然后你就有了,我把这里存入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")
于 2013-08-09T13:08:39.563 回答
4

当您删除一个对象时,您只是删除了对该对象的引用。如果该对象的引用计数降至 0,则将其从内存中删除,并带走该对象对其他对象的任何引用。

例如,字典不包含任何对象。它们所包含的只是对其他对象的引用。如果您删除对字典的所有引用,它会自动清理、删除,并且它的所有引用也会消失。字典引用的任何键或值都将看到它们的引用计数减少 1;反过来,如果该计数降至 0,它们将被清理。

因此,无需递归删除任何内容。如果您的对象不再被引用,它们会被自动清理。

请注意,即使 Python 释放对象,进程内存使用也不一定遵循。操作系统可以并且确实将内存分配给进程以减少内存流失;一个进程可能需要再次增加内存使用量,除非其他地方迫切需要内存,否则分配会保留一段时间。

于 2013-08-09T13:06:25.657 回答