python 垃圾回收机制

python 垃圾回收机制
*python采用计数机制为主,标记-清除和分代收集两种机制为辅的策略 通过引用计数来跟踪和回收垃圾; 通过标记-清除解决容器对象可能产生的循环引用问题; 通过分代回收,以空间换时间的方法提高垃圾回收效率python里每一个东西都是对象,他们的核心就是一个结构体pyobject
`pyobject是每一个对象必有的内容,ob_refcnt作为引用计数;
当一个对象有新的引用时,ob_refcnt增加;
当引用它的对象被删除,它的ob_refcnt就会减少;
ob_refcnt为0时,该对象生命结束
·引用计数机制优点:
简单
实时性:一旦没有引用,内存直接释放(不像其他机制等到特定时机)
;处理回收内存的时间分摊到了平时
·引用计数机制缺点:
维护引用计数消耗资源
维护引用计数的次数和引用赋值成正比;而不像mark and sweep等基本与回收的内存数量有关;
循环引用–内存泄漏
list1 = [] list2 = [] list1.append(list2)
list1 与list2相互引用,如果不存在其他操作,所占用内存永远无法被回收,致命;
循环引用导致内存泄漏,还会出现新的回收机制
A和B相互引用而再没有外部引用A与B中的任何一个,他们的引用计数都为1(应该被回收)
·内存泄漏:指由于疏忽或错误导致程序未能释放已经不在使用的内存的情况;
内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,
由于设计失误,失去了对该段内存的控制,因而造成了对内存的浪费;
导致程序运行速度减慢,甚至系统崩溃等严重后果
·有效防止内存泄漏问题:使用del object来删除一个对象的引用计数就可以有效防止内存泄漏问题;
通过python扩展模块gc来查看不能回收对象的详细信息;
通过sys.getrefcount(obj)来获取对象的引用计数,根据返回值是否为0判断是否内存泄漏;
!一个小的误区

a=100

sys.getrefcount(a)-1

返回结果为:50

这是为什么呢?

这是因为python系统维护着一个常见的“整数常量池”即-5-255,在这个区间的数字会有其他的处理方式,
这说明100这个数字,目前在系统中有 50 个引用。包括字符串也有一些特殊的处理,
所以在使用应用技术的时候,最好是使用自己自定义的数据类型,这样方便分析,
这也是上面为什么要自定义一个类型A的原因
·针对循环引用的解决办法:
标记清除技术–mark and sweep
分代回收技术–generation collection
手动使用gc模块

二、标记清除机制
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,
遍历以对象为节点,以引用为边构成的图,把所有可以访问到的对象打上标记,
然后清扫一遍内存空间,把所有没标记的对象释放;

针对循环引用的情况:我们有一个“孤岛”或是一组未使用的、互相指向的对象,但是谁都没有外部引用。
换句话说,我们的程序不再使用这些节点对象了,
所以我们希望Python的垃圾回收机制能够足够智能去释放这些对象并回收它们占用的内存空间。
但是这不可能,因为所有的引用计数都是1而不是0。Python的引用计数算法不能够处理互相指向自己的对象。
你的代码也许会在不经意间包含循环引用并且你并未意识到。
事实上,当你的Python程序运行的时候它将会建立一定数量的“浮点数垃圾”,
Python的GC不能够处理未使用的对象因为应用计数值不会到零。

这就是为什么Python要引入Generational GC算法的原因!

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

在这里插入图片描述
对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,
而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,
可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。
根对象就是全局变量、调用栈、寄存器。
标记清除算法作为python的辅助垃圾收集技术主要处理的是一些容器对象,比如list,dict,tuple,instance
对于字符串,数值对象不可能造成循环引用问题,python使用一个双向链表将这些容器对象组织起来。
标记清除算法的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象;

三、分代技术
分代技术的整体思想是:将系统中的所有内存快根据其存活时间划分为不同的集合,每个集合就成为一个代,
垃圾收集频率随着代的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量;
python默认定义了三代对象集合,索引数越大,对象存活时间越长
分代技术是一种典型的以空间换时间的技术,这也正是java里的关键技术,
对象存在时间越长,越可能不是垃圾,应该越少去收集;
可以减少标记清除机制带来的额外操作,分代就是将回收对象分成数个代,
每个代就是一个链表(集合),代进行标记-清除的时间与代内对象;
存活时间成正比例关系
python中一共有3代,每个代的threshold值表示该代最多容纳对象的个数,
默认情况下当0代超过100,或1,2代超过10,垃圾回收机制将触发;
0代触发将清理所有三代,1代触发清理1,2代,2代触发只清理自己;
举例: 当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,
而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,
而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,
效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,
当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。

总结:分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,
每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代)
,他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。
新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,
把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,
依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
同时,分代回收是建立在标记清除技术基础之上。
分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象.
-----------------------------------------------------------------------、
四、垃圾回收与性能调优
1.手动垃圾回收
2.调高垃圾回收阈值
3.避免循环引用(手动解循环引用和使用弱引用–一个被销毁另一个自动设置无效)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值