请注意这个简单的代码:
import random
while True:
L = list( str(random.random()))
问题:如果我让这个运行,python是否会耗尽内存?
我问的原因是:
该循环的第一次迭代,创建了一个列表,并且' L'被指定代表该列表。该循环的下一次迭代,创建另一个列表,' L'从上一个列表中拉出并分配给新列表。之前的列表已经失去了参考。以前的清单是垃圾收集的吗?如果不是在每次迭代结束时,但最终我希望?
话虽如此,只需将场景进一步扩展到多处理:
import random
while True:
l1 = list( str(random.random()))
pseudo: multiprocessing.Queue.put(l1)
# how is l1 handled here?
# is l1 .copy()-ed to the queue or referenced by the queue?
# is l1 destoryed in this process (this while loop) at the end of iteration?
答案 0 :(得分:3)
垃圾收集的主要方法是CPython中的引用计数(语言的参考实现)。当不再有对象的引用时,它占用的内存会立即释放,并且可以被其他Python对象重用。 (它可能会也可能不会被释放回操作系统。)从未释放的对象有一些例外:小整数,实习字符串(包括文字),空元组None
。
因此,为了回答您的初始问题,L
将在每次迭代时重新分配到新列表。此时,前一个列表没有引用,其内存将立即释放。
关于你的第二个例子,将某些东西放入multiprocessing
队列是必要的复制操作。该对象必须被序列化(" pickle"用Python的说法)发送到新进程,该进程有自己的内存空间,无法从原始进程的内存中看到任何内容。当您在循环中将li
重新分配到下一个列表时,之前的列表没有引用,并且将再次被释放。
在循环结束时,L
或l1
变量仍然引用一个列表:您在循环的最后一次迭代中创建的列表。如果您要发布此对象,请分别del L
或del l1
。
PS - 当对象包含对它们自己的引用时(直接或间接通过其他对象链),这被称为循环引用。这些不是通过引用计数自动收集的,Python有一个单独的垃圾收集器,它定期运行以清理它们。
答案 1 :(得分:2)
通过向类中添加自定义__del__
命令来观察发生的情况,我们可以轻松地对此进行测试:
class WithDestructor(object):
def __del__(self):
print(f"Exploding {self}")
Q=None
for i in range(5):
Q = WithDestructor()
print(f"In loop {i}")
如果清除仅发生在循环的最后,我们将得到循环输出,然后是析构函数输出。相反,我将其隔行扫描,因此Q
中的对象在重新分配Q
时立即得到清理。
In loop 0
Exploding <__main__.WithDestructor object at 0x7f93141176d8>
In loop 1
Exploding <__main__.WithDestructor object at 0x7f93141172b0>
In loop 2
Exploding <__main__.WithDestructor object at 0x7f93141176d8>
In loop 3
Exploding <__main__.WithDestructor object at 0x7f93141172b0>
In loop 4