由于您的代码,Python中是否可能发生实际的内存泄漏?

由于您的代码,Python中是否可能发生实际的内存泄漏?,第1张

由于您的代码,Python中是否可能发生实际的内存泄漏?

有可能,是的。

这取决于您在谈论哪种内存泄漏。在纯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的文档讨论了这一潜在的循环参考问题,并建议

tb
您在绝对不需要的情况下不要分配。如果您需要回溯,请考虑例如
traceback.format_exc


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

这可能以阴险的方式发生,但通常是在类范围内定义可变类型时发生的。

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('$')

可能会很快找到 这个 特定的错误,但是在这种情况下,您在类范围内放置了一个可变值,即使您在实例范围内正确访问了它,它实际上也“掉进了” 类对象

__dict__

在某些情况下(例如持有对象),这可能会导致导致应用程序堆永久增长的事情,并可能导致生产Web应用程序偶尔不会重启进程的问题。

类中的循环引用也有一个

__del__
方法。

具有讽刺意味的是,a的存在

__del__
使循环垃圾收集器无法清理实例。假设您在某处想要做一个析构函数以用于最终确定:

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

现在,它本身就可以很好地工作,并且您可能会被认为会成为确保“处置”套接字的OS资源的好管家。

但是,如果ClientConnection保留了对引用的引用,

User
而User保留了对连接的引用,那么您可能会想说在清除时,让用户取消引用该连接。但是,这实际上是缺陷:循环GC不知道正确的 *** 作顺序,也无法清除它。

解决此问题的方法是确保您进行清理,例如通过调用某种close来断开事件的连接,但是将该方法命名为而不是

__del__

实现不好的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        )

在这个人为的示例中,我们似乎正在使用某种“异步”调用,该调用将在

on_completed
完成DB调用时回叫我们(该实现本来可以保证,但最终会得到相同的结果)。

您可能没有意识到,

on_completed
闭包绑定了一个引用
self
以执行
self.profile
分配。现在,也许数据库客户端跟踪活动的查询和指向闭包完成时要调用的指针(因为它是异步的),并说它由于某种原因而崩溃。如果数据库客户端未正确清除回调等,则在这种情况下,数据库客户端现在具有对on_completed的引用,该引用具有对User的引用,该引用保持
_db
-您现在已经创建了一个可能永远不会收集的循环引用。

(即使没有循环引用,闭包也会绑定局部变量,有时甚至是实例,这有时可能会导致您认为收集的值存在很长时间,其中可能包括套接字,客户端,大缓冲区和整个事物树)

可变类型的默认参数

def foo(a=[]):    a.append(time.time())    return a

这是一个人为的示例,但

a
实际上,它是对 同一
列表的引用时,可以认为默认值是一个空列表意味着要附加它。这又可能会导致无限制的增长,而不知道您是否做到了。



欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5661602.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存