python 是否在循环中的迭代结束时进行垃圾收集?

eli*_*liu 8 python garbage-collection loops multiprocessing

请注意这个简单的代码:

    import random
    while True:
        L = list( str(random.random()))
Run Code Online (Sandbox Code Playgroud)

问题:如果我让它运行,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?
Run Code Online (Sandbox Code Playgroud)

kin*_*all 11

垃圾收集的主要方式是 CPython(该语言的引用实现)中的引用计数。当不再有对对象的任何引用时,它占用的内存会立即释放并可供其他 Python 对象重用。(它可能会也可能不会被释放回操作系统。)有一些永远不会被释放的对象的例外:小整数、内部字符串(包括文字)、空元组、None.

因此,要回答您最初的问题,L将在每次迭代时重新分配给一个新列表。此时,前面的列表没有引用,它的内存将立即释放。

关于您的第二个示例,将某些内容放入multiprocessing队列是必要的复制操作。对象必须被序列化(Python 术语中的“pickled”)才能发送到新进程,新进程有自己的内存空间,无法从原始进程的内存中看到任何内容。当您在循环中重新分配li给下一个列表时,前一个列表没有引用,并且将再次被释放。

在循环结束时,Lorl1变量仍然引用一个列表:您在循环的最后一次迭代中创建的列表。如果你想释放这个对象,只是del Ldel l1分别。

PS——当对象包含对自身的引用(直接或间接通过其他对象链)时,这被称为循环引用。这些不是通过引用计数自动收集的,Python 有一个单独的垃圾收集器,它会定期运行以清理它们。


Mic*_*son 8

我们可以通过向__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}")
Run Code Online (Sandbox Code Playgroud)

如果清理只发生在循环结束时,我们会得到循环输出,然后是析构函数输出。相反,我将它隔行扫描,因此在重新分配QQ会立即清理对象。

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
Run Code Online (Sandbox Code Playgroud)