Python垃圾回收和GC模块

Posted 梦想橡皮擦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python垃圾回收和GC模块相关的知识,希望对你有一定的参考价值。

Python垃圾回收和GC模块

Python如何处理内存管理?了解Python垃圾回收系统的来龙去脉,以及如何避免它的陷阱。

Python 为用户提供了许多便利,其中最大的便利之一就是(几乎)无障碍的内存管理。在 Python 中,不需要手动为Python中的对象和数据结构分配、跟踪和释放内存Python运行时为完成了所有这些工作,你可以专注于解决实际问题,而不是处理机器层面的细节。

尽管如此,对于经验不足的 Python 程序员来说,了解 Python 的垃圾收集和内存管理是如何工作的还是很有好处的。理解这些机制将有助于避免复杂项目出现性能问题还可以使用 Python 的内置工具来监视程序的内存管理行为。

在这篇文章中,我们将看看Python内存管理是如何工作的,它的垃圾回收系统如何帮助优化Python程序中的内存,以及如何使用标准库和第三方模块来控制内存使用和垃圾回收。

Python如何管理内存

每个 Python 对象都有一个引用计数,也被称为 refcount

refcount 是对某个对象引用其它对象总数的统计。当你增加或删除对一个对象的引用时,这个数字会上升或下降,当一个对象的refcount变为零时,该对象就会被删除,其内存被释放。

什么是引用?允许通过名称或通过另一个对象中的访问器访问对象的任何东西。

这里有一个简单的例子

x = "Hello there"

运行这段 Python代码,会发生两件事:

  1. 字符串 “Hello there” 作为一个 Python 对象被创建并存储在内存中;
  2. 在本地命名空间中创建变量 x ,并指向该对象,此时该对象的引用计数加1。

如果接下来的代码是 “y=x”,那么引用计数将再次提高到2。

每当 x 和 y 超出作用域或者从它们的名称空间中删除时,字符串x和y的引用计数都会减少1。一旦 x 和 y 都超出作用域或被删除,字符串的 refcount  就变为0并被删除。

什么是作用域

作用域是名称空间的通用术语。默认情况下,在函数内定义的变量的作用域只是该函数,但在模块级别定义的名称的作用域是整个模块。有关更多详细信息,请参阅Python的文档。

现在,假设我们创建一个包含字符串的列表,如下所示

x = ["Hello there", 2, False]

字符串一直保留在内存中,直到列表本身被删除或者包含字符串的元素从列表中被删除。这两个操作都会导致持有该字符串引用的对象消失。

现在考虑一下这个例子:

x = "Hello there"

y = [x]

如果我们从 y 中删除第一个元素,或者完全删除列表 y,那么字符串仍然在内存中这是因为 x 包含对它的引用。

Python 循环引用

大多数情况下,引用计数都是正常工作的,但有时你会遇到这样的情况:两个对象各自持有对方的一个引用,这就是所谓的循环引用。在这种情况下,对象的引用计数将永远不会达到零,它们也永远不会从内存中删除。

这里有一个人为的例子:

x = SomeClass()

y = SomeOtherClass()

x.item = y

y.item = x

由于 x 和 y 保持对彼此的引用,即使没有其它引用它们永远不会从系统中删除。

实际上,对于Python来说,为对象生成循环引用是相当常见的。一个例子是跟踪对象的异常,该对象包含对异常本身的引用。

在Python的早期版本中,具有循环引用的对象可能会随着时间积累,这对于长时间运行的应用程序来说是一个大问题。但Python后来引入了循环检测和垃圾回收系统,用于管理循环引用

Python 垃圾回收器(gc)

Python 的垃圾回收器检测具有循环引用的对象。它通过跟踪作为“容器”的对象--例如列表、字典、自定义类实例,并确定其中有哪些对象不被引用。

一旦这些对象被挑选出来,垃圾回收器就会通过它们的引用计数降低到0来删除它们。(有关这种方法的详细信息,请参阅 Python 开发人员指南。)

绝大多数 Python 对象没有循环引用,因此垃圾回收器不需要全天运行。相反,垃圾回收器使用一些方法来减少运行次数,并尽可能高效地运行。

当 Python 解释器启动时,它会跟踪已分配但未释放的对象数量绝大多数 Python 对象的生命周期非常短,因此它们很快就会出现或消失。但是随着时间的推移,长期存在的对象会逐渐积累当这种对象的数量超过一定数量时,垃圾回收器就会运行。(在 Python 3.10中,默认允许的长生命周期对象数是700。)

每次垃圾回收器运行时,它都会将收集后的所有对象放在一起,并将它们放在一个称为“分代”的组中,循环引用内,这些“第1代”对象被扫描的频率较低。任何在垃圾回收器中幸存下来的第1代对象最终都会迁移到第2代,在第2代中,它们很少被扫描。

同样,并不是所有的对象都会被垃圾回收器追踪到,例如像用户创建类这样的复杂对象总是被跟踪的,但是一个只保存简单对象,例如整数和字符串的字典不会被跟踪,因为在那个特定的字典中没有对象持有对其的引用,不持有对其它元素的引用的简单对象,如整数和字符串,永远不会被跟踪。

如何使用GC模块

一般来说,垃圾回收器不需要调整就可以运行良好,Python 的开发团队选择了常见情况的默认值,如果你确实需要调整垃圾回收的工作方式,你可以使用 Python 的 gc 模块,gc 模块为垃圾回收器的行为提供了编程接口,并可配置对哪些对象进行跟踪。

gc让你做的一件有用的事情是,当你确定不需要垃圾回收器的时候,可以关掉它。如果你有一个短期运行的脚本,堆积了大量的对象,你就不需要垃圾回收器。所有的东西都会在脚本结束时被清除掉。为此,你可以用gc.disable()命令禁用垃圾回收器,之后可以用gc.enable()重新启用它。

你还可以使用gc.collect() 手动运行垃圾回收,这方面的一个常见应用是管理程序中生成许多临时对象的部分,你可以在程序的这一部分禁用垃圾回收,然后在结束时手动运行回收并重新启用回收。

另一个有用的垃圾回收优化是 gc.free()运行该代码后,垃圾回收器跟踪的所有内容都被“冻结”,或者被列为免于回收扫描这样,未来的扫描可以跳过这些对象。如果有一个导入库并在启动前设置大量内部状态的程序,那么可以在完成所有工作之后发出 gc.free()。这样可以防止垃圾回收器搜寻那些无论如何都不可能被移除的东西。(如果希望冻结的对象再次执行垃圾回收,请使用 gc.unfree()。)

使用gc调试垃圾回收

还可以使用 gc 调试垃圾回收行为如果内存中堆积的对象数量过多,而且没有被垃圾回收,那么可以使用 gc 的检查工具来确定哪些对象保存着对这些对象的引用。

如果想知道哪些对象保存着对给定对象的引用,可以使用 gc.get_reference (obj)来列出它们还可以使用 gc.get_reference (obj)查找给定对象引用的任何。

如果不确定给定对象是否是垃圾回收的候选对象,gc.is_trace (obj)会告诉垃圾回收器是否跟踪该对象如前所述,请记住垃圾回收器不会跟踪“原子”对象(如整数)或仅包含原子对象的元素。

如果希望亲自查看正在收集的对象,可以使用 gc.set_debug(gc.DEBUG _ LEAK | gc.DEBUG_STATS)设置垃圾收集器的调试标志。这将有关垃圾收集的信息写入 stderr它将所有作为垃圾收集的对象保存在只读列表gc.garbage

避免Python内存管理中的陷阱

如前所述,对象可能堆积在内存中,如果在某个地方仍然有对它们的引用,则不会被回收。这并不是 Python 的垃圾回收本身的问题而是因为垃圾回收器无法判断是否意外地保留了对某些内容的引用。

让我们以一些防止对象出现永不回收提示来结束本文

注意对象作用域

如果你把对象1指定为对象2的一个属性(比如一个类),对象2需要在对象1之前退出作用范围

obj1 = MyClass()

obj2.prop = obj1

更重要的是,如果这是以其它操作的副作用形式方式发生的,例如把对象2作为参数传递给对象1的构造函数,你可能没有意识到对象1有一个引用。


obj1 = MyClass(obj2)

另一个例子,如果你把一个对象推到一个模块级的列表中,然后忘记了这个列表,这个对象将一直存在,直到从列表中删除,或者直到列表本身不再有任何引用。但是如果这个列表是一个模块级的对象,它很可能会一直存在,直到程序终止。

简而言之,要意识到对象可能被另一个不明显的对象引用。

使用weakref避免循环引用

Python 的weakref 模块让你创建对其它对象的弱引用,弱引用不会增加一个对象的引用数,所以一个只有弱引用的对象是垃圾回收的候选对象。

weakref的一个常见用途是对象的缓存,如果不希望被引用的对象仅仅因为有一个缓存条目而被保留下来,可以对缓存条目使用弱引用。

手动中断循环引用

最后,如果你知道一个给定的对象持有对另一个对象的引用,你可以手动中断对该对象的引用,如果你有instance_of_class.ref = other_object,当你准备移除instance_of_class时,你可以设置instance_of_class.ref = None

  • 📢📢📢📢📢📢
  • 💗 你正在阅读【梦想橡皮擦】的博客
  • 👍 阅读完毕,可以点点小手赞一下
  • 🌻 发现错误,直接评论区中指正吧
  • 📆 橡皮擦的第 747 篇原创博客

从订购之日起,案例5年内保证更新


[⭐️ Python 爬虫 120,点击订购 ⭐️  ]
[⭐️ 爬虫100例教程,点击订购 ⭐️ ]

以上是关于Python垃圾回收和GC模块的主要内容,如果未能解决你的问题,请参考以下文章

Python垃圾回收

Python垃圾回收和GC模块

Python使用gc模块进行垃圾回收

python__高级 : GC垃圾回收相关

Python的垃圾回收机制

python 垃圾回收机制