【发布时间】:2012-11-17 15:46:06
【问题描述】:
这两个函数以基本相同的方式计算相同的东西(整数的个数,使得关联的 Collatz 序列的长度不大于 n)。唯一的区别是第一个只使用集合,而第二个同时使用集合和列表。
第二个泄漏内存(至少在 Python 3.2 的 IDLE 中),第一个没有,我不知道为什么。我尝试了一些“技巧”(例如添加del 语句),但似乎没有任何帮助(这并不奇怪,这些技巧应该没用)。
如果有人能帮助我了解发生了什么,我将不胜感激。
如果您想测试代码,您可能应该使用 55 到 65 范围内的 n 值,任何高于 75 的值几乎肯定会导致(完全预期的)内存错误。
def disk(n):
"""Uses sets for explored, current and to_explore. Does not leak."""
explored = set()
current = {1}
for i in range(n):
to_explore = set()
for x in current:
if not (x-1) % 3 and ((x-1)//3) % 2 and not ((x-1)//3) in explored:
to_explore.add((x-1)//3)
if not 2*x in explored:
to_explore.add(2*x)
explored.update(current)
current = to_explore
return len(explored)
def disk_2(n):
"""Does exactly the same thing, but Uses a set for explored and lists for
current and to_explore.
Leaks (like a sieve :))
"""
explored = set()
current = [1]
for i in range(n):
to_explore = []
for x in current:
if not (x-1) % 3 and ((x-1)//3) % 2 and not ((x-1)//3) in explored:
to_explore.append((x-1)//3)
if not 2*x in explored:
to_explore.append(2*x)
explored.update(current)
current = to_explore
return len(explored)
EDIT :在使用解释器的交互模式(没有 IDLE)时也会发生这种情况,但在直接从终端运行脚本时不会发生这种情况(在这种情况下,内存使用量会恢复正常)函数返回后的时间,或在明确调用 gc.collect() 后的时间。
【问题讨论】:
-
如何测量内存泄漏?您是否尝试在 IDLE 等交互式环境之外检查它?
-
显示您的泄漏测量值。在这两种情况下,您都在分配列表和集合,在这两种情况下,所有这些对象都可以通过激活函数来访问,因此它们不会被垃圾回收。
-
@Marcin :问题不在于函数执行时的内存使用情况。这与您所期望的差不多(上升趋势,当
current或to_explore的某些旧版本被垃圾收集时偶尔会下降)。问题是在disk_2返回后内存没有被释放(即使你明确地调用gc.collect())。对于 n=65,使用量峰值为 800MB,并在disk_2返回后保持在 ~200MB。 -
@user36732 “内存未释放”是什么意思?你的意思是过程映像不会占用更少的内存吗?如果是这样,那可能是因为您在 linux 上运行它,对吧?
-
1) 如果您正在运行 CPython,则 gc.collect() 在这里无关紧要,因为您不会创建任何引用循环(这些只是
set()和list()与数字对吗?)。在这种情况下,一旦对象失去最后一个引用,CPython 就会“收集”对象。 2)我们仍然不知道您如何衡量内存使用情况,这可能是问题的症结所在。
标签: python python-3.x