Python 2.6的垃圾回收似乎清理了对象,但内存未释放

3 投票
2 回答
2959 浏览
提问于 2025-04-16 11:31

我有一个用Python 2.6写的程序,它会创建很多短暂存在的实例(这就是经典的生产者-消费者问题)。我发现,当这些实例被创建时,内存使用量在top和pmap中显示似乎在增加,而且从来没有下降。我担心我用的某个Python模块可能在泄漏内存,所以我仔细检查了我的代码,想要找出问题所在。然后,我尽量简化代码,重现这个问题。最后我写出了这样一段代码:

class LeaksMemory(list):
    timesDelCalled = 0

    def __del__(self):
        LeaksMemory.timesDelCalled +=1


def leakSomeMemory():
    l = []
    for i in range(0,500000):
        ml = LeaksMemory()
        ml.append(float(i))
        ml.append(float(i*2))
        ml.append(float(i*3))
        l.append(ml)

import gc
import os


leakSomeMemory()

print("__del__ was called " + str(LeaksMemory.timesDelCalled) + " times")
print(str(gc.collect())  +" objects collected")
print("__del__ was called " + str(LeaksMemory.timesDelCalled) + " times")
print(str(os.getpid()) + " : check memory usage with pmap or top")

如果你用类似 'python2.6 -i memoryleak.py' 的命令运行它,程序会暂停,你可以用 pmap -x PID 来检查内存使用情况。我加了del方法,这样我可以确认垃圾回收(GC)是否在进行。实际上在我的程序中并没有这个方法,它似乎也没有影响功能。每次调用 leakSomeMemory() 都会增加这个程序消耗的内存。我担心我可能犯了什么简单的错误,导致一些引用意外地被保留,但我找不到具体原因。

2 个回答

4

根据 Alex Martelli 的说法:

“确保大量但临时使用的内存在完成后能够将所有资源返回给系统的唯一可靠方法,就是让这些使用发生在一个子进程中,这个子进程负责处理内存消耗大的工作,然后结束。”

所以,在你的情况下,使用 multiprocessing 模块来在不同的进程中运行短暂的函数是有意义的,这样可以确保当进程结束时资源能够被释放。

import multiprocessing as mp

def NOT_leakSomeMemory():
    # do stuff
    return result


if __name__=='__main__':
    pool = mp.Pool()
    results=pool.map(NOT_leakSomeMemory, range(500000)) 

想了解更多关于如何使用 multiprocessing 设置的想法,可以查看 Doug Hellman 的教程

8

Python会释放对象,但不会立即把内存还给操作系统。相反,它会在同一个解释器中重新使用这些内存区域,以便将来分配使用。

这里有一篇关于这个问题的博客文章: http://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htm

更新:我自己用Python 2.6.4测试过,没发现内存使用量持续增加。有些调用leakSomeMemory()会让Python进程的内存占用增加,而有些则会让它减少。所以这完全取决于内存分配器是如何重新使用内存的。

撰写回答