python中的垃圾回收(GC)机制


Posted in Python onSeptember 21, 2020

一、引用计数

Python 垃圾回收以引用计数为主,分代回收为辅。引用计数法的原理是每个对象维护一个ob_refcnt,用来记录对象被引用的次数,也就是用来追踪有多少个引用指向了对象,当发生以下四种情况的时候,对象的引用计数+1:

  • 对象被创建,比如:a = 14
  • 对象被引用,比如: b = a
  • 对象被作为参数,传给函数,比如:func(a)
  • 对象作为容器中的一个元素,比如:List = {a, ”a” , ”b”, 2}

与上述情况相对应,当发生以下四种情况时,对象的引用计数-1:

对象的别名被显式销毁,比如:del a
对象的别名被赋予新的对象,比如:a = 26
对象离开它的作用域,比如 func() 执行完毕时,函数里面的所有局部变量的引用计数都会减 1
将元素从容器中删除,或者容器被销毁
当对象的引用计数为 0 时,它将被 Python 虚拟机回收。

在 Python 中一切皆对象,它们的核心是 Py_Object 结构体,所有 Python 对象的头部都包含该结构:

// object.h
#define PyObject_HEAD   
 _PyObject_HEAD_EXTRA  
 Py_ssize_t ob_refcnt;  
 struct _typeobject *ob_type;
​
typedef struct _object {
 PyObject_HEAD
} PyObject;

比如 int 类型的定义如下:

// intobj.h
typedef struct {
 PyObject_HEAD
 long ob_ival;
} PyIntObject;

简而言之,PyObject 是每个对象必有的内容,其中 ob_refcnt 是对象的引用计数。对象有新的引用时,它的 ob_refcnt 会增加;当对象的引用被删除时,ob_refcnt 会减少。当引用计数为 0 时,对象的生命周期就结束了。

// object.h
#define Py_INCREF(op) (    
 _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA 
 ((PyObject*)(op))->ob_refcnt++)
​
#define Py_DECREF(op)     
 do {      
 if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA 
 --((PyObject*)(op))->ob_refcnt != 0)  
  _Py_CHECK_REFCNT(op)   
 else      
 _Py_Dealloc((PyObject *)(op));   
 } while (0)

引用计数有很明显的优点:

  • 高效
  • 运行期没有停顿,即实时性:对象一旦没有引用,将直接被释放。实时性还带来一个好处是:处理回收内存的时间分摊到了平时
  • 对象有确定的生命周期
  • 易于实现

原始的引用计数法也有明显的缺点:

  • 维护引用计数消耗资源,维护引用计数的次数和引用赋值成正比
  • 无法解决循环引用的问题

比如:

list1 = []
list2 = []
list1.append(list2)
list2.append(list1)

为了解决这两个致命弱点,Python 又引入了以下两种 GC 机制。

二、标记-清除

『标记-清除(Mark-Sweep)』算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记阶段,GC 会给所有『活动对象』打上标记;第二阶段是回收没有标记的『非活动对象』。那么 GC 如何判断哪些是活动对象、哪些是非活动对象呢?

对象之间通过引用(指针)连在一起,构成一个有向图。对象是有向图的顶点,引用关系是有向图的弧。从根对象(root object)出发,遍历有向图,将可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象是全局变量、调用栈、寄存器。

python中的垃圾回收(GC)机制

在上图中,把小黑圈视为全局变量,也就是把它作为 root object,从小黑圈出发,对象 1 可直达,那么它将被标记,对象 2、3 可间接到达,也会被标记,而 4 和 5 不可达,因此 1、2、3 是活动对象,4 和 5 是非活动对象,会被 GC 回收。

标记清除算法作为 Python 的辅助垃圾回收技术,主要用于处理容器对象,比如 list、dict、tuple、instance 等,因为字符串、数值等原子类型的对象不可能造成循环引用问题。Python 使用双向链表将容器对象组织起来。不过这种简单粗暴的标记清除算法也有明显的缺点:清除非活动对象前,必须顺序扫描整个堆内存,哪怕只剩下小部分非活动对象,也要扫描所有对象。

三,分代回收

分代回收是一种以空间换时间的操作方式,Python 将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python 将内存分为了 3 代,分别为年轻代(第 0 代)、中年代(第 1 代)、老年代(第 2 代),它们对应是 3 个链表,垃圾回收频率随着对象存活时间的增大而减小。新创建的对象都会被分配到年轻代,当年轻代链表的节点总数达到上限时,Python 垃圾收集机制就会被触发,把可以被回收的对象回收掉,而不能被回收的对象会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至存活于整个系统的生命周期内。分代回收建立在标记清除的基础之上,分代回收同样作为 Python 处理容器对象的辅助垃圾回收技术。

以上就是python中的GC机制的详细内容,更多关于python GC的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python遍历指定文件及文件夹的方法
May 09 Python
python处理二进制数据的方法
Jun 03 Python
Python正则表达式完全指南
May 25 Python
Python实现中文数字转换为阿拉伯数字的方法示例
May 26 Python
Python实现通讯录功能
Feb 22 Python
python函数不定长参数使用方法解析
Dec 14 Python
Django集成celery发送异步邮件实例
Dec 17 Python
OpenCV Python实现拼图小游戏
Mar 23 Python
keras-siamese用自己的数据集实现详解
Jun 10 Python
python 判断一组数据是否符合正态分布
Sep 23 Python
如何用Python提取10000份log中的产品信息
Jan 14 Python
python软件测试Jmeter性能测试JDBC Request(结合数据库)的使用详解
Jan 26 Python
如何在Python3中使用telnetlib模块连接网络设备
Sep 21 #Python
总结Pyinstaller的坑及终极解决方法(小结)
Sep 21 #Python
python生成xml时规定dtd实例方法
Sep 21 #Python
Python中的特殊方法以及应用详解
Sep 20 #Python
matplotlib 三维图表绘制方法简介
Sep 20 #Python
Python三维绘图之Matplotlib库的使用方法
Sep 20 #Python
scrapy利用selenium爬取豆瓣阅读的全步骤
Sep 20 #Python
You might like
php利用腾讯ip分享计划获取地理位置示例分享
2014/01/20 PHP
PHP 中 DOMDocument保存xml时中文出现乱码问题的解决方案
2016/09/19 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
2017/09/16 PHP
ASP中进行HTML数据及JS数据编码函数
2009/11/11 Javascript
JS Replace 全部替换字符的用法小结
2013/12/24 Javascript
javascript中this的四种用法
2015/05/11 Javascript
使用JavaScript制作一个简单的计数器的方法
2015/07/07 Javascript
JavaScript设计模式开发中组合模式的使用教程
2016/05/18 Javascript
在localStorage中存储对象数组并读取的方法
2016/09/24 Javascript
jQuery实现复选框的全选和反选
2017/02/02 Javascript
Jquery鼠标放上去显示全名的实现方法
2017/02/06 Javascript
Bootstrap模态窗口源码解析
2017/02/08 Javascript
JS失效 提示HTML1114: (UNICODE 字节顺序标记)的代码页 utf-8 覆盖(META 标记)的冲突的代码页 utf-8
2017/06/23 Javascript
js实现移动端导航点击自动滑动效果
2017/07/18 Javascript
JavaScript实现快速排序的方法分析
2018/01/10 Javascript
详解vue-cli 2.0配置文件(小结)
2019/01/14 Javascript
Vue + Elementui实现多标签页共存的方法
2019/06/12 Javascript
node解析修改nginx配置文件操作实例分析
2019/11/06 Javascript
[01:37]DOTA2超级联赛专访ChuaN 传奇般的电竞之路
2013/06/19 DOTA
Python中使用socket发送HTTP请求数据接收不完整问题解决方法
2015/02/04 Python
python实现简单ftp客户端的方法
2015/06/28 Python
Python的Django框架中的数据过滤功能
2015/07/17 Python
python实现中文转换url编码的方法
2016/06/14 Python
Python中循环引用(import)失败的解决方法
2018/04/22 Python
Python一个简单的通信程序(客户端 服务器)
2019/03/06 Python
python中查看.db文件中表格的名字及表格中的字段操作
2020/07/07 Python
美国汽配连锁巨头Pep Boys官网:轮胎更换、汽车维修服务和汽车零部件
2017/01/14 全球购物
Scotch Porter官方网站:男士美容产品
2020/08/31 全球购物
linux面试题参考答案(7)
2014/07/24 面试题
J2EE面试题集锦(附答案)
2013/08/16 面试题
英文版网络工程师求职信
2013/10/28 职场文书
幼儿园大班教学反思
2014/02/10 职场文书
公司建议书怎么写
2014/05/15 职场文书
夫妻房产协议书的格式
2014/10/11 职场文书
学校团代会开幕词
2016/03/04 职场文书
全网非常详细的pytest配置文件
2022/07/15 Python