Python垃圾回收和GC模块
Python垃圾回收和GC模块
Python如何处理内存管理?了解Python垃圾回收系统的来龙去脉,以及如何避免它的陷阱。
Python 为用户提供了许多便利,其中最大的便利之一就是(几乎)无障碍的内存管理。在 Python 中,不需要手动为Python中的对象和数据结构分配、跟踪和释放内存,Python运行时为你完成了所有这些工作,你可以专注于解决实际问题,而不是处理机器层面的细节。
尽管如此,对于经验不足的 Python 程序员来说,了解 Python 的垃圾收集和内存管理是如何工作的还是很有好处的。理解这些机制将有助于避免复杂项目出现性能问题,还可以使用 Python 的内置工具来监视程序的内存管理行为。
在这篇文章中,我们将看看Python内存管理是如何工作的,它的垃圾回收系统如何帮助优化Python程序中的内存,以及如何使用标准库和第三方模块来控制内存使用和垃圾回收。
Python如何管理内存
每个 Python 对象都有一个引用计数,也被称为 refcount。
refcount 是对某个对象引用其它对象总数的统计。当你增加或删除对一个对象的引用时,这个数字会上升或下降,当一个对象的refcount变为零时,该对象就会被删除,其内存被释放。
什么是引用?允许通过名称或通过另一个对象中的访问器访问对象的任何东西。
这里有一个简单的例子:
x = "Hello there"
运行这段 Python代码,会发生两件事:
- 字符串 “Hello there” 作为一个 Python 对象被创建并存储在内存中;
- 在本地命名空间中创建变量 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 = yy.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。
-
Python垃圾回收和GC模块相关推荐
- 垃圾回收(三)-gc模块
一.垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅. 1.导致引用计数+1的情况 对象被创建,例如a=23 对象被引用,例如b=a 对象被作为参数,传入到一个函数中,例如func ...
- Java-JVM虚拟机内存垃圾回收机制gc入门:引用类型,对象标记算法,回收算法,常见的 garbage collector
文章目录 GC的优缺点 引用的四种类型 对象标记算法 引用计数法 可达性分析法 回收算法 标记-清除算法(Mark-Sweep) 复制算法 标记-整理算法(Mark-Compact) 分代收集算法 常 ...
- Python垃圾回收(gc)拖累了程序执行性能?
起因 前段时间,在做文本处理的实验时,需要预加载大量的原始数据(100W),在Python中使用的字典(dict)类型负责保存这些数据,很快就开发完成 了一个Demo版,然而程序执行的效率不是那么令人 ...
- python垃圾回收离职_垃圾回收gc.md
垃圾回收gc python的垃圾收回机制不想c和c++是开发者自己管理维护内存的,python的垃圾回收是系统自己处理的,所以作为普通的开发者,我们不需要关注垃圾回收部分的内容,如果想要深层次理解py ...
- python垃圾回收 (GC) 机制
Python 能够自动进行内存分配和释放,但了解 python 垃圾回收 (garbage collection, GC) 的工作原理可以帮助你写出更好更快的 Python 程序.Python 使用两 ...
- Python垃圾回收机制详解
一.垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅.引用计数的缺陷是循环引用的问题. 在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存. #e ...
- 【Python】学习笔记总结5(Python垃圾回收及内存优化)
文章目录 五.Python垃圾回收 1.引用计数(主) 2.GC负责的主要任务 3.标记-清除 4.引用计数不能回收的案例分析 5.分代收集 6.GC的阈值 7.GC垃圾回收自动触发机制 7.内存优化 ...
- python 垃圾回收详解
原文:https://zhuanlan.zhihu.com/p/31150408 总纲 策略和垃圾回收系统工作内容 引用计数详解 标记-清除+分代收集 循环引用 编程应用-常见方法 ex 过程详解 使 ...
- 你应该了解的python 垃圾回收机制
引用计数器回收 一个对象,会记录着自身被引用的个数 每增加一个引用,这个对象的引用计数会自动+1 每减少一个引用,这个对象的引用计数会自动-1 查看引用计数 import sys sys.getref ...
最新文章
- 2021年大数据常用语言Scala(十四):基础语法学习 数组 重点掌握
- C#Winform+WindowsAPI做个剪贴板无缝自动保存器(视频截图利器)
- Silverlight WCF RIA服务(三十四)身份验证、角色、个性化 5
- ubuntu 命令整合1
- oracle拆分字段为多行,一句话实现字段拆分成多行
- 最全前端开发面试问题及答案整理
- CF1406D:Three Sequences(贪心、构造)
- 华为笔记本计算机在哪,新一代华为MateBook 的机会在哪里?
- 案例解析|从数据规划、业务分析到管理决策的数据治理方案
- java调用webservice_Oracle触发器调用webservice说明
- ANDROID框架揭秘
- qlistview 自定义控件_Qt之QListView使用
- 单龙芯3A3000-7A1000PMON研究学习-(1)硬件原理图
- 论文翻译:2021_DeepFilterNet: A Low Complexity Speech Enhancement Framework for Full-Band Audio based on
- VSCode Clang代码自动格式化
- 项目管理之需求变更:化解程序员的“头号噩梦”
- 报错解决:There-is-no-PasswordEncoder-mapped-for-the-id-null
- acca计算机管理会计,acca与管理会计的八大区别在哪
- 维控触摸屏通过U盘升级工程教程
- 物联网名词和MQTT解释,阿里云的教程:用51单片机做一个物联网温度计+远程开关
热门文章
- 垃圾回收(三)-gc模块