小编典典

Python:垃圾回收失败?

python

考虑以下脚本:

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

分别 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

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

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


更新资料

正如本文所建议的,可能是解释者应受责备:

这是因为您已经同时创建了500万个整数,每个int对象占用12个字节。“为了速度”,Python为整数对象维护了一个内部空闲列表。不幸的是,这份免费清单是不朽的,而且规模不限。花车还使用不朽且不受限制的自由名单。

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


更新2

Python实现可能确实是这种情况。找到此答案可以最终解释问题和可能的解决方法。

不幸的是(取决于您的Python版本和版本),某些类型的对象使用“自由列表”,这是一种整洁的局部优化,但可能会导致内存碎片,特别是通过使越来越多的“内存”仅用于特定类型的对象而引起的内存碎片。因此无法使用“普通基金”。

确保大量但临时使用内存的唯一确实可靠的方法是在完成后将所有资源都返还给系统,这是让使用发生在子进程中,该进程需要大量内存,然后终止。在这种情况下,操作系统将完成其工作,并乐意回收子进程可能吞没的所有资源。幸运的是,该multiprocessing模块使这种操作(过去很痛苦)在现代版本的Python中还不错。

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


阅读 225

收藏
2020-12-20

共1个答案

小编典典

发现这也可以由Alex Martelli在另一个话题中回答。

不幸的是(取决于您的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()
2020-12-20