最近被python的内存使用搞得比较的郁闷,记录在案。

 

内存泄漏的经典定义是曾经使用过一次的内存,现在却没有,但还没有被回收。使用纯Python代码几乎不可能。但正如Antoine指出的那样,即使您不需要保留所有数据,您也可以通过允许数据结构无限制地增长来轻松地消耗所有内存。

1 将值存储在类或全局范围而不是实例范围中,而不是实现它。

比如下面的这个代码:

class Money(object):
    name = ''
    symbols = []   # This is the dangerous line here

    def set_name(self, name):
        self.name = name

    def add_symbol(self, symbol):
        self.symbols.append(symbol)


m = Money()
m.set_name('Dollar')
m.add_symbol('$')

那么正确的写法应该是如何呢?

class Money(object):
    self.name = ''
    self.symbols = []   # This is the dangerous line here

    def set_name(self, name):
        self.name = name

    def add_symbol(self, symbol):
        self.symbols.append(symbol)

2 sys.exc_info()带来的循环引用

问题代码如下:

while game.running():
    try:
        key_press = handle_input()
    except SomeException:
        etype, evalue, tb = sys.exc_info()
        # Do something with tb like inspecting or printing the traceback

这里面我们可能认为tb是一个临时变量,但是实际上,tb包含了handle_input运行的上下文信息。如果game一直在执行中,那么tb依然不会被释放,哪怕是下一次执行handle_input。

3 在类中自己实现__del__方法

class ClientConnection(...):
    def __del__(self):
        if self.socket is not None:
            self.socket.close()
            self.socket = None


现在这个工作正常,你可能会认为它是操作系统资源的良好管理者,以确保套接字被“处置”。 但是,如果ClientConnection保留引用说明,用户和用户保留对连接的引用,您可能会想要说清理时,让用户取消引用连接。这实际上是一个缺陷:循环GC不知道正确的操作顺序,也无法清理它。 对此的解决方案是确保你通过调用某种类型的关闭来断开事件,但是将该方法命名为__del__以外的其他方法。


4 非正常使用C库

 

在Python中,您相信垃圾收集器会丢弃您不使用的内容。但是,如果使用包装C库的C扩展,则大多数时候您负责确保明确关闭或取消分配资源。大多数情况下都记录了这一点,但是习惯于不必进行此显式取消分配的python程序员可能会丢弃句柄(如从函数或其他任何内容返回)到该库而不知道资源被保留。