gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 20:34:44 24 4
gpt4 key购买 nike

我没有代码示例,但我很好奇是否可以编写导致内存泄漏的 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' 也会保持事件状态,甚至可能永远存在。 docs for 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__方法。

具有讽刺意味的是,存在 __del__使循环垃圾收集器无法清理实例。假设您有一些事情要为完成目的执行析构函数:
class ClientConnection(...):
def __del__(self):
if self.socket is not None:
self.socket.close()
self.socket = None

现在这本身就可以正常工作,并且您可能会相信它是操作系统资源的好管家,以确保套接字被“处理”。

但是,如果 ClientConnection 保留一个引用说, User并且用户保留了对连接的引用,您可能会想说,在清理时,让用户取消引用连接。 This is actually the flaw ,但是:循环 GC 不知道正确的操作顺序并且无法清理它。

解决这个问题的方法是确保你通过调用某种关闭来清除事件,但将该方法命名为 __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 给我们回电。当数据库调用完成时(实现可能是 promise ,它最终得到相同的结果)。

您可能没有意识到 on_completed闭包绑定(bind)对 self 的引用为了执行 self.profile任务。现在,也许 DB 客户端会跟踪事件查询和指向闭包的指针,当它们完成时(因为它是异步的)并说它因任何原因崩溃。如果数据库客户端没有正确清理回调等,在这种情况下,数据库客户端现在有一个对 on_completed 的引用,它有一个对用户的引用,该用户保留了 _db - 您现在已经创建了一个可能永远不会被收集的循环引用。

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

可变类型的默认参数
def foo(a=[]):
a.append(time.time())
return a

这是一个人为的例子,但可以让人相信默认值 a当它实际上是对同一列表的引用时,作为一个空列表意味着附加到它。这可能会再次导致无限增长,而您不知道您这样做了。

关于python - 由于您的代码,Python 中是否可能存在实际内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2017381/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com