Python:垃圾收集失败了吗?

时间:2012-03-08 11:34:14

标签: python garbage-collection

考虑以下脚本:

l = [i for i in range(int(1e8))]
l = []
import gc
gc.collect()
# 0
gc.get_referrers(l)
# [{'__builtins__': <module '__builtin__' (built-in)>, 'l': [], '__package__': None, 'i': 99999999, 'gc': <module 'gc' (built-in)>, '__name__': '__main__', '__doc__': None}]
del l
gc.collect()
# 0

重点是,在所有这些步骤之后,我的机器上的这个python进程的内存使用率大约是30%(Python 2.6.5,请求的更多细节?)。 这是top的输出的摘录:

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND  
5478 moooeeeep 20   0 2397m 2.3g 3428 S    0 29.8   0:09.15 ipython  

RESP。 ps aux

moooeeeep 5478  1.0 29.7 2454720 2413516 pts/2 S+   12:39   0:09 /usr/bin/python /usr/bin/ipython gctest.py

根据gc.collect的{​​{1}}:

  

由于特定的实施,某些免费列表中的所有项目都不会被释放,特别是intfloat

这是否意味着,如果我(暂时)需要大量不同的intfloat数字,我需要将其导出到C / C ++,因为Python GC无法释放内存?


更新

正如the docs所暗示的那样,解释员可能应该受到责备:

  

这是你同时创建了500万个整数,每个int对象消耗12个字节。 “为了速度”,Python维护整数对象的内部空闲列表。不幸的是,这个免费清单既是不朽的,也是无限的。花车也使用不朽的&amp;无限的免费清单。

然而问题仍然存在,因为我无法避免这些数据(来自外部源的时间戳/值对)。我真的被迫放弃Python并回到C / C ++吗?


更新2

可能确实是这样,Python实现会导致问题。发现this article最终解释了问题和可能的解决方法。

4 个答案:

答案 0 :(得分:7)

您的回答可能是here

  

Python做了很多分配和解除分配。所有物品,   包括整数和浮点数等“简单”类型存储在   堆。为每个变量调用malloc和free会非常慢。   因此,Python解释器使用各种优化的内存   分配方案。最重要的是malloc实现   名为pymalloc,专门用于处理大量的   小额拨款。任何小于256字节的对象都使用它   分配器,而更大的东西使用系统的malloc。 :此   实现永远不会将内存返回给操作系统。代替,   如果再次需要,它会坚持下去。这是有效的   它会在短时间内再次使用,但如果长时间使用则会浪费   在需要之前通过。

答案 1 :(得分:7)

我做了一些测试,这个问题只发生在CPython 2.x.这个问题在CPython 3.2.2中消失了(它回到了新解释器的内存使用),而PyPy 1.8(python 2.7.2)也降低到了与新的pypy进程相同的水平。

所以不,你不需要换成另一种语言。但是,可能有一种解决方案不会迫使您切换到不同的Python实现。

答案 2 :(得分:6)

发现这也是回答by Alex Martelli in another thread

  

不幸的是(取决于你的Python版本和版本),某些类型的对象使用“自由列表”,这是一个简洁的本地优化,但可能会导致内存碎片,特别是通过为一个对象创建更多的内存“专用”某种类型,因而“普通基金”无法使用。

     

确保大量但临时使用内存的唯一真正可靠的方法是在完成后将所有资源返回给系统,就是在子进程中使用该进程,这会占用大量内存,然后终止工作。在这种情况下,操作系统将完成其工作,并乐意回收子进程可能已经吞噬的所有资源。幸运的是,多处理模块在现代版本的Python中进行这种操作(过去相当痛苦)并不算太糟糕。

     

在您的用例中,似乎子进程积累一些结果并确保主进程可用的结果的最佳方法是使用半临时文件(半临时我的意思是,不是那种关闭时自动消失的文件,只有你完成后明确删除的普通文件)。

幸运的是,我能够将内存密集型工作拆分为单独的块,这使得解释器在每次迭代后实际释放临时内存。我使用以下包装器将内存密集型函数作为子进程运行:

import multiprocessing

def run_as_process(func, *args):
    p = multiprocessing.Process(target=func, args=args)
    try:
        p.start()
        p.join()
    finally:
        p.terminate()

答案 3 :(得分:0)

Python倾向于相当智能地进行垃圾收集,根据我的经验释放内存就好了。考虑到它确实有一个小的开销(我的大约15Mb),但除此之外,内存要求与C没有什么不同。如果你处理的是如此多的数据,那么内存是一个严重的问题,你可能会去在C中遇到同样的问题,因此尝试更改数据处理方式会好得多,例如将其存储在页面文件中并一次使用一个可管理的卡盘。