Python单调增加的内存使用(泄漏?)

9 投票
2 回答
3459 浏览
提问于 2025-04-16 22:25

我在用一段简单的代码,发现内存使用量一直在增加。我在用一个小模块把东西存到磁盘上。我注意到这个问题出现在unicode字符串上,而不是整数上,我是不是做错了什么?

当我执行:

>>> from utils.diskfifo import DiskFifo
>>> df=DiskFifo()
>>> for i in xrange(1000000000):
...     df.append(i)

内存消耗是稳定的

但是当我执行:

>>> while True:
...     a={'key': u'value', 'key2': u'value2'}
...     df.append(a)

内存使用量就飙升了。有没有什么建议?下面是这个模块的代码...


import tempfile
import cPickle

class DiskFifo:
    def __init__(self):
        self.fd = tempfile.TemporaryFile()
        self.wpos = 0
        self.rpos = 0
        self.pickler = cPickle.Pickler(self.fd)
        self.unpickler = cPickle.Unpickler(self.fd)
        self.size = 0

    def __len__(self):
        return self.size

    def extend(self, sequence):
        map(self.append, sequence)

    def append(self, x):
        self.fd.seek(self.wpos)
        self.pickler.dump(x)
        self.wpos = self.fd.tell()
        self.size = self.size + 1

    def next(self):
        try:
            self.fd.seek(self.rpos)
            x = self.unpickler.load()
            self.rpos = self.fd.tell()
            return x

        except EOFError:
            raise StopIteration

    def __iter__(self):
        self.rpos = 0
        return self

2 个回答

0

为了补充combatdave@的回答:

我直接绕过了pickle中糟糕的内存缓存,因为在读取时清除缓存似乎是不可能的,这导致了明显的内存泄漏。pickle的流式处理似乎是为了读取和写入中等大小的文件设计的,而不是为了处理无限的数据流。

所以我就用了以下这些简单的工具函数:

def framed_pickle_write(obj, stream):
    serial_obj = pickle.dumps(obj)
    length = struct.pack('>I', len(serial_obj))
    stream.write(length)
    stream.write(serial_obj)


def framed_pickle_read(stream):
    data = stream.read(4)
    length, = struct.unpack('>I', data)
    serial_obj = stream.read(length)
    return pickle.loads(serial_obj)
15

pickler模块会把它见过的所有对象都存储在一个叫做“备忘录”的地方,这样就不需要重复处理同样的东西。如果你想跳过这个步骤(也就是说,不想在你的pickler对象中保存对这些对象的引用),你可以在保存之前先清空这个备忘录:

def append(self, x):
    self.fd.seek(self.wpos)
    self.pickler.clear_memo()
    self.pickler.dump(x)
    self.wpos = self.fd.tell()
    self.size = self.size + 1

来源:http://docs.python.org/library/pickle.html#pickle.Pickler.clear_memo

补充说明:你实际上可以通过使用下面的追加函数,观察到备忘录的大小在你保存对象时是如何增加的:

def append(self, x):
    self.fd.seek(self.wpos)
    print len(self.pickler.memo)
    self.pickler.dump(x)
    self.wpos = self.fd.tell()
    self.size = self.size + 1

撰写回答