有可能,是的。
这取决于您在谈论哪种内存泄漏。 在纯python代码中,不可能像C语言那样“忘记释放”内存,但是有可能将引用悬挂在某个地方。 这样的一些例子:
一个未处理的回溯对象,即使该函数不再运行,该对象仍可以使整个堆栈帧保持活动状态
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中一直发生的所有堆栈的帧信息,一直到调用它为止。 假设您的游戏继续进行,则即使在您下次调用handle_input时,此“ tb”也将保持有效,甚至可能永远存在。 现在,exc_info的文档讨论了这个潜在的循环参考问题,如果您并非绝对需要,建议不要分配a。 如果您需要回溯,请考虑 a
将值存储在类或全局范围而不是实例范围中,而不实现它。
这可能以阴险的方式发生,但通常是在类范围内定义可变类型时发生的。
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('$')
您可能会很快找到这个特定的错误,但是在这种情况下,您在类范围内放置了一个可变值,即使您在实例范围内正确访问了它,它实际上也“掉进了”类对象的a。
在某些情况下(例如持有对象),这可能会导致导致应用程序堆永久增长的事情,并可能导致生产Web应用程序偶尔不会重启进程的问题。
类中的循环引用也具有a方法。
具有讽刺意味的是,a的存在使循环垃圾收集器无法清理实例。 假设您在某些地方想要做一个析构函数以用于最终确定:
class ClientConnection(...):
def __del__(self):
if self.socket is not None:
self.socket.close()
self.socket = None
现在,它本身可以很好地工作,并且您可能会被认为是确保“处置”套接字是OS资源的良好管家。
但是,如果ClientConnection保留对a的引用,而User保留对连接的引用,则您可能会想说在清除时,让用户取消引用该连接。 但是,这实际上是缺陷:循环GC不知道正确的操作顺序,也无法清除它。
解决此问题的方法是确保您进行清理,例如通过调用某种关闭来断开事件,但是将该方法命名为a以外的其他名称。
实现不好的C扩展,或者不能正确使用C库。
在Python中,您信任垃圾收集器丢弃不使用的东西。 但是,如果您使用包装了C库的C扩展名,则大多数时候您将负责确保显式关闭或取消分配资源。 多数情况下,这已被记录在案,但是习惯于不必进行这种显式取消分配的python程序员可能会在不知道资源被占用的情况下,将句柄(例如从函数或任何东西返回)扔给了该库。
包含闭包的作用域包含的数量超出了您的预期
class User:
def set_profile(self, profile):
def on_completed(result):
if result.success:
self.profile = profile
self._db.execute(
change={'profile': profile},
on_complete=on_completed
)
在这个人为的示例中,我们似乎正在使用某种“异步”调用,该调用将在完成DB调用后回叫给我们a(实现可以实现,其结果相同)。
您可能没有意识到,a闭包将引用绑定到self,以便执行self.profile分配。 现在,也许数据库客户端会跟踪活动查询和指向闭包完成时要调用的指针(因为它是异步的),并说它由于任何原因而崩溃。 如果数据库客户端未正确清除回调等,则在这种情况下,数据库客户端现在具有对on_completed的引用,该引用具有对User的引用,该引用保留了_db-您现在已经创建了一个可能永远不会被收集的循环引用。
(即使没有循环引用,闭包也会绑定局部变量,有时甚至是实例,这有时可能会导致您认为收集的值存在很长时间,其中可能包括套接字,客户端,大缓冲区和整个事物树)
可变类型的默认参数
def foo(a=[]):
a.append(time.time())
return a
这是一个人为的示例,但是可以使人们相信,默认值a是一个空列表,意味着在它实际上是对同一列表的引用的同时附加了它。 这又可能导致无限制的增长,而您不知道自己这样做了。