2

当我尝试在 python 解释器中运行以下代码时,它给了我一个明显的 MemoryError,因为我正在运行一个无限循环以检查内存使用情况。

def a():
 i=2
 while True:
  yield i
  i*=i

print sum(a())

当我运行这段代码时,我可以看到 python 的内存使用量在增长。但是,当我收到 MemoryError 时,我可以看到 python 解释器进程仍然持有大约 200 MB 的内存,即使 sum 函数不再在解释器中运行。我的问题是:python 的内置函数是否不应该非常高效,即使在垃圾收集方面也是如此?内置程序不应该将垃圾收集的责任交给用户,而不是清理自己的混乱吗?

4

3 回答 3

3

如果 python 内置有内存泄漏,它是一个错误。

然而,仅仅因为 python 仍然持有内存并不意味着存在内存泄漏。Python 有各种有趣的分配技巧,因此它可能会保留您的内存以备将来使用。所以你不能简单地在任务管理器中查看 python 是否正确处理内存。

要检查实际的内存泄漏,一种技术是重复运行您的代码。如果它是真正的内存泄漏,则每次运行它时都应该丢失内存。如果它是幻像内存泄漏,内存实际上将被第二次重用,并且您不会丢失任何内存。

我在我的 Linux 机器上运行了你的代码,虽然我确实发现它在运行该循环一段时间而不是之前使用了更多的内存,但再次运行它并没有使用额外的内存,实际上一些内存“丢失”在第一次运行中似乎恢复了。

于 2012-08-30T18:22:31.857 回答
2

免费列表上关于 GC 的 Python 文档:

由于特定的实现,尤其是 int 和 float,并非某些空闲列表中的所有项目都可能被释放。

因此,通过生成器分配一个无穷大的整数会吃掉你所有的内存(直到接收到内存错误),而并不是所有的项目都可以按照上面的定义被释放。

但那段记忆就这样消失了吗?不,环境会保留它以供您的代码重用。垃圾收集“高效”并不意味着它会在对象离开范围的那一刻进行回收。它也可能意味着“让我们继续使用刚刚使用的内存,也许这段愚蠢的代码会想要再次使用它。”

或者正如Effbot告诉我们的那样:

返回给给定分配器的内存将被该分配器重用,即使它没有返回给系统。

可以 强制进行 GC 收集,但这实际上可能会影响性能,除非您知道原因并且有非常非常好的理由强制它。

gc.collect([generation])
With no arguments, run a full collection. The optional argument generation may be an integer specifying which generation to collect (from 0 to 2). A ValueError is raised if the generation number is invalid. The number of unreachable objects found is returned.

Changed in version 2.5: The optional generation argument was added.

Changed in version 2.6: The free lists maintained for a number of built-in types are cleared whenever a full collection or collection of the highest generation (2) is run. Not all items in some free lists may be freed due to the particular implementation, in particular int and float.
于 2012-08-31T12:09:05.620 回答
1

sum这与、 生成器或垃圾收集器的实现无关。

您看到的内存使用量不断增长是保持i. iPython 支持任意大的整数,并且每次迭代存储双倍所需的位数。如果你改变你的改变你的生成器功能是:

def a():
    i=2
    while True:
        yield i
        i += 1

你会看到内存占用是稳定的。

于 2012-08-30T18:47:24.147 回答