Python的垃圾回收机制(引用计数+标记清除+分代回收)
Python的垃圾回收机制(引用计数+标记清除+分代回收)
一、写在前面:
我们都知道Python一种面向对象的脚本语言,对象是Python中非常重要的一个概念。在Python中数字是对象,字符串是对象,任何事物都是对象,而它们的核心就是一个结构体--PyObject。
typedef struct_object{
int ob_refcnt;
struct_typeobject *ob_type;
}PyObject;
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。
二、垃圾回收机制
垃圾回收(Garbage Collection)大家应该多多少少都听过,但是什么是垃圾回收呢?我们这里说的垃圾回收肯定不是把垃圾丢进垃圾桶。现在的高级语言Java,C#等,都采用了垃圾回收机制,而不再是C,C++里用户自己管理维护内存的方式,自己管理内存是很自由,但是可能出现内存泄漏,悬空指针等问题。而垃圾回收机制作为现代编程语言的自动内存管理机制,专注于两件事:1. 找到内存中无用的垃圾资源 2. 清除这些垃圾并把内存让出来给其他对象使用。
三、Python中的垃圾回收
在Python中,垃圾回收机制主要是以引用计数为主要手段,以标记清除和分代回收机制作为辅助手段实现的。
1、引用计数
通过前面的介绍,我们已经知道PyObject是每个对象必有的内容,而当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少,当引用计数为0时,该对象生命就结束了。
我们来看看引用计数+1的情况有什么:
(1)对象被创建:
这里实际上123这个对象并没有在内存中新建,因为在Python启动解释器的时候会创建一个小整数池,在-5~256之间的整数对象会被自动加载到内存中等待调用。因此a=123是对123这个整数对象增加了一次引用。而456是不在整数池里的,需要创建对象,那么最后的引用次数是2呢?因为sys.getrefcount(b)也是一次引用。
(2)对象被引用:
每一次赋值操作都会增加数据的引用次数,要记住引用的变量a、b、c指向的是数据456,而不是变量本身。
(3)对象作为参数传递到函数中:
这里可以很明显看到在被传递到函数中后,引用计数增加了1。
(4)对象作为元素储存到容器中:
这里我们在创建对象之后,把a分别添加到了一个列表和一个元组中,引用计数都增加了。
虽然引用计数必须在每次分配合释放内存的时候加入管理引用计数的操作,然而与其他垃圾回收技术相比,引用计数有一个最大的优点,那就是“实时性”,如果这个对象没有引用,内存就直接释放了,而其他垃圾回收技术必须在某种特殊条件下才能进行无效内存的回收。但是引用计数带来的维护引用计数的额外操作和Python中进行的内存分配和释放,引用的赋值次数成正比的。除此之外,引用计数机制的还有一个最大的软肋--无法解决循环引用带来的问题。循环引用可以使一种引用对象的引用计数不为0,然而这些对象实际上并没有被任何外部对象所引用,它们之间只是相互引用,这意味着这组对象所占用的内存空间是应该被回收的,但是由于循环引用导致的引用计数不为0,所以这组对象所占用的内存空间永远不会被释放。如下,list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,所占用的内存永远无法被回收,这将是致命的。
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
2、标记清除
标记清除(Mark—Sweep)算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记阶段,GC会把所有的活动对象打上标记,第二阶段是把那些没有标记的对象非活动对象进行回收。
对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。
在上图中,可以从程序变量直接访问块1,并且可以间接访问块2和3。程序无法访问块4和5。第一步将标记块1,并记住块2和3以供稍后处理。第二步将标记块2,第三步将标记块3,但不记得块2,因为它已被标记。扫描阶段将忽略块1,2和3,因为它们已被标记,但会回收块4和5。
标记清除算法作为Python的辅助垃圾收集技术,主要处理的是一些容器对象,比如list、dict、tuple等,因为对于字符串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。不过,这种简单粗暴的标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。
3、分代回收
分代回收是建立在标记清除技术基础之上的,是一种以空间换时间的操作方式。
Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
原文地址https://www.cnblogs.com/TM0831/p/10599716.html
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
PHP中双引号引起的命令执行漏洞
PHP中双引号引起的命令执行漏洞在PHP语言中,单引号和双引号都可以表示一个字符串,但是对于双引号来说,可能会对引号内的内容进行二次解释,这就可能会出现安全问题。 0|1正文举个简单例子 <?php$a = 1;$b = 2;echo '$a$b';//输出结果为$a$becho "$a$b";//输出结果为12?>可以看到这两个输出的结果并不相同。 在双引号中倘若有${}出现,那么{}内的内容将被当做代码块来执行。 可以看到成功执行了phpinfo() 试想一下,倘若在一个cms的后台,可以修改数据库的配置文件,且配置文件中的值用双引号包括,我们虽然也可以直接闭合代码达到getshell的后果,但是如果cms对传递的参数进行了addlashes()处理的话,我们就无法去闭合代码了,但这时我们可以传入${命令}就可以达到getshell的目的。 现在,让我们来修改一下代码,让我们不只能输出phpinfo <?php echo "${@assert($_POST[a])}";?> //@是用来防止输出错误信息的 菜刀成功连接 0|1总结本文简单记录了双引号可能会引...
- 下一篇
大数据学习资料下载,新手攻略,数据分析工具、软件使用教程
大数据被认为是“未来的新石油”,在社会生产、流通、分配、消费活动以及经济运行机制等方面发挥着重要的作用。 作为 IT 类职业中的“大熊猫”,大数据工程师的收入待遇可以说达到了同类的顶级。国内 IT、通讯、行业招聘中,有 10% 都是和大数据相关的,且比例还在上升。“大数据时代的到来很突然,在国内发展势头激进,而人才却非常有限,现在完全是供不应求的状况。 所有从底层数据工作者往上发展的基本路径: 第一阶段(一般岗位叫数据专员) 基本学会excel(VBA最好学会;会做透视表;熟练用筛选、排序、公式),做好PPT。这样很多传统公司的数据专员已经可以做了 第二阶段(数据专员~数据分析师) 这一阶段要会SQL,懂业务,加上第一阶段的那些东西。大多数传统公司和互联网小运营、产品团队够用了。 第三阶段(数据分析师) 统计学熟练(回归、假设检验、时间序列、简单蒙特卡罗),可视化,PPT和excel一定要溜。这些技术就够了,能应付大多数传统公司业务和互联网业务。 第四阶段(分裂) 数据分析师(数据科学家)、BI等:这部分一般是精进统计学,熟悉业务,机器学习会使用(调参+选模型+优化),取数、ETL、可...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2全家桶,快速入门学习开发网站教程
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,7,8上安装Nginx,支持https2.0的开启