为什么将列表附加到自身,然后删除会导致内存泄漏
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
标志,其中明确地告诉垃圾收集器保留无法访问的对象!
我找到了这个 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
标志,其中明确地告诉垃圾收集器保留无法访问的对象!