在Python中强制进行垃圾回收以释放内存

16

我有一个使用大量 dict 对象的 Python2.7 应用程序,其中大多数包含字符串作为键和值。

有时这些字典和字符串不再需要,我想从内存中删除它们。

我尝试了不同的方法,例如 del dict[key]del dict 等等。但应用程序仍然使用相同数量的内存。

以下是一个我期望能够释放内存的示例。但它并没有做到 :(

import gc
import resource

def mem():
    print('Memory usage         : % 2.2f MB' % round(
        resource.getrusage(resource.RUSAGE_SELF).ru_maxrss/1024.0/1024.0,1)
    )

mem()

print('...creating list of dicts...')
n = 10000
l = []
for i in xrange(n):
    a = 1000*'a'
    b = 1000*'b'
    l.append({ 'a' : a, 'b' : b })

mem()

print('...deleting list items...')

for i in xrange(n):
    l.pop(0)

mem()

print('GC collected objects : %d' % gc.collect())

mem()

输出:

Memory usage         :  4.30 MB
...creating list of dicts...
Memory usage         :  36.70 MB
...deleting list items...
Memory usage         :  36.70 MB
GC collected objects : 0
Memory usage         :  36.70 MB

我希望在这里能够“收集”一些对象并释放一些内存。

我是否做错了什么?有没有其他方法来删除未使用的对象或至少找出意外使用对象的位置。


3
那么请执行 gc.collect() - muddyfish
2个回答

30

Frederick Lundh解释

如果您创建了一个大型对象并将其删除,Python可能已经释放了内存,但涉及的内存分配器不一定会将内存返回给操作系统,因此看起来Python进程使用的虚拟内存比实际使用的要多得多。

而Alex Martelli则写道:

确保一个大但临时使用的内存在完成后将所有资源都返回给系统的唯一真正可靠的方法是,在子进程中进行这种使用,该子进程执行耗费内存的工作,然后终止。

因此,您可以使用 multiprocessing 来生成子进程,执行占用大量内存的计算,然后确保在子进程终止时释放内存:

import multiprocessing as mp
import resource

def mem():
    print('Memory usage         : % 2.2f MB' % round(
        resource.getrusage(resource.RUSAGE_SELF).ru_maxrss/1024.0,1)
    )

mem()

def memoryhog():
    print('...creating list of dicts...')
    n = 10**5
    l = []
    for i in xrange(n):
        a = 1000*'a'
        b = 1000*'b'
        l.append({ 'a' : a, 'b' : b })
    mem()

proc = mp.Process(target=memoryhog)
proc.start()
proc.join()

mem()
产出。
Memory usage         :  5.80 MB
...creating list of dicts...
Memory usage         :  234.20 MB
Memory usage         :  5.90 MB

我认为这不可能通过我们的应用程序(作为快速修复)实现。许多对象是共享的,并且需要在应用程序的不同部分进行访问。但是在您的示例中,第二个“Mem usage…”应该会产生更多的内存,您不觉得吗? - ddofborg
Python内存分配非常复杂。它为对象创建内存池,除非整个池为空且未被分段,否则无法将内存池返回给操作系统。此外,许多内置类型保留先前分配的项目的“空闲列表”,以便将来重新使用相同类型的内存分配,而不是创建新的内存分配。 - Matt Anderson
@ddofborg:我的机器上的 getrusage 报告的最大驻留集大小是以千字节为单位,而不是字节。 - unutbu
@MattAnderson 好的,我明白这很复杂。你是在暗示处理大量数据后没有办法将内存释放给操作系统吗? - ddofborg
2
@ddofborg - 在深入研究了cpython 2.7内存管理之后,我的团队的生产级策略是将任何需要大量内存处理的操作放在自己的进程中并退出,让操作系统在完成后清理它。尽量保持“命令和控制”进程的合理大小(在我们的情况下为500MB-1GB)。是否可以将内存返回给操作系统取决于分配了哪些对象以及内存有多么碎片化。从阅读Python 3.x版本说明来看,这种情况在3系列解释器中可能已经得到改善。 - Matt Anderson
由于OP标记为Python2.7,我要补充一下:在Python v2.x中,多进程可能会对无法正确分叉的有效负载对象(如多线程类对象)造成一些问题。我确信我并没有完全理解为什么,但我见过线程应用程序的锁被“不正确”地分叉,导致多进程子进程可能会悄悄失败。这可能会导致进程无限运行,并创建一些非常有趣的问题。当然,比我聪明的人肯定能够恰当地解释它,但是确保您清除了子进程是值得的。 - DaveL17

2

使用多进程和名为Ray的库可能会有所帮助,它使用共享内存在进程之间执行多GB数据共享。这种方法易于生成辅助进程,并且仍然可以从父进程快速轻松地访问相同的对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接