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 相关文章推荐
Eclipse + Python 的安装与配置流程
Mar 05 Python
python中的__init__ 、__new__、__call__小结
Apr 25 Python
python中pycurl库的用法实例
Sep 30 Python
简单说明Python中的装饰器的用法
Apr 24 Python
Python遍历指定文件及文件夹的方法
May 09 Python
Python2.x与Python3.x的区别
Jan 14 Python
Python正则表达式完全指南
May 25 Python
新手常见6种的python报错及解决方法
Mar 09 Python
深入了解和应用Python 装饰器 @decorator
Apr 02 Python
python实现邮件发送功能
Aug 10 Python
Python使用matplotlib绘制圆形代码实例
May 27 Python
Python爬虫实现自动登录、签到功能的代码
Aug 20 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中的PHP_EOL换行符详细解析
2013/10/26 PHP
php制作动态随机验证码
2015/02/12 PHP
PHP实现批量重命名某个文件夹下所有文件的方法
2017/09/04 PHP
用jQuery打造TabPanel效果代码
2010/05/22 Javascript
JavaScript 高级篇之DOM文档,简单封装及调用、动态添加、删除样式(六)
2012/04/07 Javascript
jQuery的slideToggle方法实例
2013/05/07 Javascript
Jquery动态改变图片IMG的src地址示例
2013/06/25 Javascript
Javascript表单验证要注意的事项
2014/09/29 Javascript
嵌入式iframe子页面与父页面js通信的方法
2015/01/20 Javascript
原生js实现的贪吃蛇网页版游戏完整实例
2015/05/18 Javascript
JS实现两表格里数据来回转移的方法
2015/05/28 Javascript
基于jQuery实现多层次的手风琴效果附源码
2015/09/21 Javascript
Node.js实现兼容IE789的文件上传进度条
2016/09/02 Javascript
vue教程之toast弹框全局调用示例详解
2020/08/24 Javascript
AngularJS中table表格基本操作示例
2017/10/10 Javascript
Angularjs按需查询实例代码
2017/10/30 Javascript
小程序视频列表中视频的播放与停止的示例代码
2018/07/20 Javascript
微信小程序制作表格的方法
2019/02/14 Javascript
vue与django集成打包的实现方法
2019/11/11 Javascript
Vue列表循环从指定下标开始的多种解决方案
2020/04/08 Javascript
[51:36]EG vs VP 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
Python的Flask站点中集成xhEditor文本编辑器的教程
2016/06/13 Python
Python正则简单实例分析
2017/03/21 Python
浅谈pytorch和Numpy的区别以及相互转换方法
2018/07/26 Python
解决python执行不输出系统命令弹框的问题
2019/06/24 Python
python Event事件、进程池与线程池、协程解析
2019/10/25 Python
解决PyCharm IDE环境下,执行unittest不生成测试报告的问题
2020/09/03 Python
HTML5 embed 标签使用方法介绍
2013/08/13 HTML / CSS
介绍一下Ruby的多线程处理
2013/02/01 面试题
幼儿园中班新学期寄语
2014/01/18 职场文书
一年级家长会邀请函
2014/01/25 职场文书
环保小标语
2014/06/13 职场文书
我们的节日中秋活动方案
2014/08/19 职场文书
2014年心理健康教育工作总结
2014/12/06 职场文书
2015年行政工作总结范文
2015/04/09 职场文书
生日祝酒词大全
2015/08/10 职场文书