为什么将列表附加到自身,然后删除会导致内存泄漏

Why appending list to itself, and then deleting, results in memory leak

我找到了这个 memory leak detection snippet 并且想知道它产生的内存泄漏。

import gc

def dump_garbage():
    """
    show us what's the garbage about
    """

    # force collection
    print("\nGARBAGE:")
    gc.collect()

    print("\nGARBAGE OBJECTS:")
    for x in gc.garbage:
        s = str(x)
        if len(s) > 80: s = s[:80]
        print(type(x),"\n  ", s)

if __name__=="__main__":
    import gc
    gc.enable()
    gc.set_debug(gc.DEBUG_LEAK)

    # make a leak
    l = []
    l.append(l)
    del l

    # show the dirt ;-)
    dump_garbage()

为了测试gc内存泄漏检测,作者自己创建了一个小内存泄漏:

l = []
l.append(l)
del(l)

为什么会导致泄漏?如我所见,我会有一个列表对象,而不是一个嵌套列表对象,其中内部 外部,而不是删除外部。 gc 不会知道删除所有对原始列表的引用,从而导致内部列表泄漏吗?

很可能它与无限循环非常相似: 虽然 l = []l.append(l) 编译器在识别列表是 [][[]] 还是 [[[]]] 时遇到问题,因为列表附加到自身。

我希望你能理解,你可以在这里看我的shell例子:

>>> l = []
>>> l.append(l)
>>> l
[[...]]

#Then, again:

>>> l = []
>>> l.append(l)
>>> l.append(l)
>>> l

[[...], [...]]

>>> l[0], l[1]

([[...], [...]], [[...], [...]])

>>> l[0][0]

[[...], [...]]

>>> l[0][0][0]

[[...], [...]]    # and so on...

因此,您可以看到 l 基本上是无限列表,删除无限列表将导致内存泄漏(我不知道原因,但如果您愿意,可以深入挖掘。 ..)

希望这对您有所帮助。

编辑:
shell 真正输出文本 [...],以防万一你想知道。

(将我之前的评论编辑成答案。)

链接的文章是2001年的。那时候,Python 2.x是新的,很多人可能还在用Python 1.x。

Python 1.x 完全依赖垃圾收集的引用计数,循环引用是失败时的教科书示例。 Python 2.x 添加了一个循环检测步骤,但是还有一些遗留问题,例如存在 __del__ 阻塞垃圾收集器的方法,因为它无法找出正确的顺序破坏。从 Python 3.4 开始,most of the wrinkles have been ironed out.

那么为什么示例代码仍然指示泄漏?它设置 GC_DEBUG_LEAK 标志,其中明确地告诉垃圾收集器保留无法访问的对象!