【问题标题】:Why does Python garbage collection with threads not work?为什么带有线程的 Python 垃圾收集不起作用?
【发布时间】:2020-06-14 15:32:30
【问题描述】:

当我将线程定义为实例变量时,定义的其他对象不会被垃圾回收。在此示例中,在循环的每次迭代中都会创建一个数组 buffer,该数组永远不会被垃圾回收:

import array
import gc
import threading

from pympler import muppy

class A():
  def __init__(self):
    self.buffer = array.array('B')
    # Defining a thread keeps array in memory
    self.thread = threading.Thread(target=lambda *_: None)

if __name__ == '__main__':
  for i in range(10):
    a = A()
    # del a  # needed
    gc.collect()
    print('Iteration {}:'.format(i))
    obj = muppy.get_objects()
    print('Array objects {}'.format(len(muppy.filter(obj, Type=array.ArrayType))))
    print('Thread objects {}'.format(len(muppy.filter(obj, Type=threading.Thread))))
    print('Running threads {}'.format(len(threading.enumerate())))

输出是:

Iteration 0:
Array objects 1
Thread objects 2
Running threads 1
Iteration 1:
Array objects 2
Thread objects 3
Running threads 1
...

线程是否启动+加入无关紧要。 显式删除对象 bufferself.thread 允许垃圾回收。我无法理解这种行为,并希望得到一些解释。 在我的生产代码中,此功能最终会导致 python 实例的内存不足终止。

【问题讨论】:

    标签: python multithreading memory-leaks garbage-collection


    【解决方案1】:

    obj = muppy.get_objects() 是内存中所有对象的列表(因此是对它们的引用)。

    由于在以下迭代中执行垃圾收集覆盖obj 变量之前,muppy.get_objects() 操作变得累积 - 我在使用 @987654324 时也陷入了一个陷阱@。

    简而言之:

    • first gc.collect():不影响我们的变量
    • 首先obj = ...muppy 看到a 持有的线程引用
    • 第二个gc.collect():第一轮创建的A实例不再被a引用,但仍被obj引用=>不能被垃圾回收
    • 第二个obj = ...:muppy 看到a 引用的新线程和obj 引用的旧线程
    • ...

    使用muppy 的经验法则:在再次调用muppy.get_objects() 之前,请务必删除对对象列表的引用,否则您可能会感到惊讶。

    在循环末尾添加一个简单的del obj 会导致

    Iteration 0:
    Array objects 1
    Thread objects 2
    Running threads 1
    Iteration 1:
    Array objects 1
    Thread objects 2
    Running threads 1
    Iteration 2:
    Array objects 1
    Thread objects 2
    Running threads 1
    ...
    

    附:这与线程无关。

    【讨论】:

    • 感谢您的解释,在这个最小的示例中,它看起来确实是一个使用错误的工具。我不明白的一件事:如果它与线程无关;为什么删除 self.thread = 行有帮助?
    • 如果你删除self.thread = 行,你会得到一个线程的引用(不是你明确创建的线程;我猜是运行脚本的线程)。您不再创建新线程,因此线程对象的数量保持不变。
    • P.S.添加了对这里发生的事情的分步说明;希望能解决问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    • 1970-01-01
    • 1970-01-01
    • 2011-03-31
    • 1970-01-01
    • 2010-12-19
    • 2014-08-08
    相关资源
    最近更新 更多