【问题标题】:Why appending list to itself, and then deleting, results in memory leak为什么将列表附加到自身,然后删除会导致内存泄漏
【发布时间】:2019-11-04 05:47:19
【问题描述】:

我发现了这个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 不知道要删除对原始列表的所有引用,从而导致内部列表泄露吗?

【问题讨论】:

  • 这篇文章是 2001 年的。Python 1.x 仅依靠引用计数进行 GC,循环引用是失败时的教科书示例。 Python 2 引入了一个循环检测步骤,应该可以应对这种情况。还存在一些问题,例如防止 GC 的 __del__ 方法的存在,但我相信它们也已在 Python 3 的后续版本中得到解决。
  • 垃圾仍然在 python 3.x 中显示,所以没有...
  • 问题中的sn-p设置了GC_DEBUG_LEAK,这意味着GC_DEBUG_SAVEALL,意思是“所有不可达的对象都会被追加到垃圾中而不是被释放”。如果我运行python3 -c 'for k in range(1000000000): l = [k]; l.append(l),尽管分配了数百万个循环列表,但进程的大小永远不会超过大约 7 MB。
  • @TurePålsson 我已经删除了错误的答案。但是你可以用你的评论来回答。

标签: python memory-leaks


【解决方案1】:

(将我之前的 cmets 编辑为答案。)

链接的文章是 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 标志,其中明确告诉垃圾收集器保留无法访问的对象!

【讨论】:

  • 所以,在 py3. 4+,不设置这个标志,gc会知道收集这个列表并释放它?
  • 是的。 (我必须在这里多写一些东西才能使评论足够长。)
【解决方案2】:

很可能它与无限循环非常相似: 虽然 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 真正输出文本[...],以防万一你想知道。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 2017-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多