最近被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程序员可能会丢弃句柄(如从函数或其他任何内容返回)到该库而不知道资源被保留。