上一篇文章: 私有化规则与属性Property
下一篇文章: Python进程专题总览篇
高级语言通常都有垃圾回收机制,其中c、c++使用的是用户本身管维护内存的方式,这种方式比较自由,但若是回收不当也会引发垃内存泄露等问题。而python采用的是引用计数机制为主,标记-清理和分代收集两种机制为辅的策略。python
python中一切皆对象,因此python底层计数结构地就能够抽象为:c++
引用计数结构体{ 引用计数; 引用的对象 }
是否是简单明了。如今咱们先去考虑一下,什么状况下引用计数+1,什么状况下-1,当引用次数为0时,确定就是须要进行回收的时刻。segmentfault
一、对象被建立时,例如 mark="帅哥" 二、对象被copy引用时,例如 mark2=mark,此时mark引用计数+1 三、对象被做为参数,传入到一个函数中时 四、对象做为一个子元素,存储到容器中时,例如 list=[mark,mark2]
一、对象别名被显示销毁,例如 del mark 二、对象引用被赋予新的对象,例如mark2=mark3,此时mark引用计数-1(对照引用计数+1的状况下的第二点来看) 三、一个函数离开他的做用域,例如函数执行完成,它的引用参数的引用计数-1 四、对象所在容器被销毁,或者从容器中删除。
实例:app
import sys a = "mark 帅哥" print(sys.getrefcount(a))
结果:编辑器
4
备注:若是实际结果与上面不符,跟使用的编辑器有很大关系,重点是理解计数引用原理,不要太在乎为啥不是1.函数
想理解缘由能够转看:https://stackoverflow.com/questions/45021901/why-does-a-newly-created-variable-in-python-have-a-ref-count-of-fourcode
一、简单、直观 二、实时性,只要没有了引用就释放资源。
一、维护引用计数须要消耗必定的资源 二、循环应用时,没法回收。也正是由于这个缘由,才须要经过标记-清理和分代收集机制来辅助引用计数机制。
由上面内容咱们能够知道,引用计数机制有两个缺点,缺点1还能够勉强让人接受,缺点2若是不解决,确定会引发内存泄露,为了解决这个问题,引入了标记删除。对象
咱们先来看个实例,从实例中领会标记删除:进程
a=[1,2]#假设此时a的引用为1 b=[3,4]#假设此时b的引用为1 #循环引用 a.append(b)#b的引用+1=2 b.append(a)//a的引用+1=2 假如如今须要删除a,应该如何回收呢? c=[5,6]#假设此时c的引用为1 d=[7,8]#假设此时d的引用为1 #循环引用 c.append(d)#c的引用+1=2 d.append(c)#d的引用+1=2 假如如今须要同时删除c、d,应该如何回收呢?
首先咱们应该已经知道,无论上面两种状况的哪个都没法只经过计数来完成回收,由于随便删除一个变量,它的引用只会-1,变成1,仍是大于0,不会回收,为了解决这个问题,开始看标记删除来大展神威吧。内存
puthon标记删除时经过l两个容器来完成的:死亡容器、存活容器。 首先,咱们先来分析状况2,删除c、d 删除后,c的引用为1,d的引用为1,根据引用计数,还没法删除 标记删除第一步:对执行删除操做后的每一个引用-1,此时c的引用为0,d的引用为0,把他们都放到死亡容器内。把那些引用仍然大于0的放到存活容器内。 标记删除第二步:遍历存活容器,查看是否有的存活容器引用了死亡容器内的对象,若是有就把该对象从死亡容器内取出,放到存活容器内。 因为c、d都没有对象引用他们了,因此通过这一步骤,他们仍是在死亡组。 标记删除第三部:将死亡组全部对象删除。 这样就完成了对从c、d的删除。
一样道理,咱们来分析:只删除a的过程:
标记删除第一步:对执行删除(-1)后的每一个引用-1,那么a的引用就是0,b的引用为1,将a放到死亡容器,将b放到存活容器。 标记删除第二步:循环存活容器,发现b引用a,复活a:将a放到存活容器内。 标记删除第三步:删除死亡容器内的全部对象。
综上所说,发现对于循环引用,必须将循环引用的双发对象都删除,才能够被回收。
标记-清理就是这么简单,😀。
通过上面的【标记-清理】方法,已经能够保证对垃圾的回收了,但还有一个问题,【标记-清理】何时执行比较好呢,是对全部对象都同时执行吗?
同时执行很显然不合理,咱们知道,存活越久的对象,说明他的引用更持久(好像是个屁话,引用不持久就被删除了),为了更合理的进行【标记-删除】,就须要对对象进行分代处理,思路很简单:
一、新建立的对象作为0代 二、没执行一个【标记-删除】,存活的对象代数就+1 三、代数越高的对象(存活越持久的对象),进行【标记-删除】的时间间隔就越长。这个间隔,江湖人称阀值。
是否是很简单呢。
一、调用gc.collect() 二、GC达到阀值时 三、程序退出时
因为整数使用普遍,为了不为整数频繁销毁、申请内存空间,引入了小整数对象池。[-5,257)是提早定义好的,不会销毁,单个字母也是。
那对于其余整数,或者其余字符串的不可变类型,若是存在重复的多个,例如:
a1="mark" a2="mark" a3="mark" a4="mark" .... a1000="mark"
若是每次声明都开辟出一段空间,很显然不合理,这个时候python就会使用intern机制,靠引用计数来维护。
总计:
一、小整数[-5,257):共用对象,常驻内存 二、单个字符:共用对象,常驻内存 三、单个单词等不可变类型,默认开启intern机制,共用对象,引用计数为0时销毁。