Python垃圾回收(GC)
Posted yongzyw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python垃圾回收(GC)相关的知识,希望对你有一定的参考价值。
Python中的GC算法
- 分为一下三点:
- 引用计数
- 标记-清除
- 分代回收
- 简述:
- Python中的GC模块主要运用了引用计数来追踪和回收垃圾.在引用计数的基础上,还可以通过"标记-清除"解决容器对象可能产生的循环引用的问题.通过分代回收以空间换取时间进一步提交垃圾回收的效率
- 标记-清除:
- 标记-清除的出现打破了循环引用,也就是它只关注那些可能会产生循环引用的对象.
- 缺点:该机制所带来的额外操作和需要回收的内存成正比.
- 分代回收:
- 将系统中的所有内存根据其存活时间划分为不同的集合,每一个集合成为一个"代",垃圾收集的频率随着‘代‘的存活时间的增大而减小,也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率.
- 那么如何衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果是一个对象经过的垃圾手机次数越多,可以得出:该对象存活时间就越长.
下面对于三种回收机制进行具体解释:
引用计数(主要)
- 在Python中万物皆对象.在Python中每一个对象的核心就是一个结构体PyObject,它的内部有一个引用计数器(ob_refcnt)
引用计数的意思就是,一个对象在被New方法创建出来的时候因为被New方法引用,所以他的引用计数就是1.如果他被其他对象引用(例如b=a,被丢入函数列表等待就会在引用计数上加1),如果引用它的对象被删除(在之前的基础上DEL b)那么它的引用计数就会减少,知道引用计数变为0,垃圾回收机制就会将它回收.
优/缺点:
- 简单,实时性
缺点:
维护性高(简单实时,但额外占用了一部分资源,虽然逻辑简单,但是麻烦)
不能解决的问题:循环引用
a=[1,2] b=[2,3] a.append(b) b.append(a) DEL a DEL b
说实话感觉有点像死锁问题,这种问题出现在可以循环的结构中LIst,Dict,Object等待,如果代码a,b之间的引用都为1,而a,b被引用的对象删除后各自减去1(所以他们各自的引用计数还是1)这种情况就无法解决了,也就为我们引入了下面的主题:标记-清除
标记-清除
标记清除就是用来解决循环引用的问题的,只有容器对象才会出现引用循环,比如列表,类,字典,元组.首先,为了追踪容器对象,需要每个容器对象维护两个额外的指针,用来将而容器对象组成一个链表,指针分别指向前后两个容器对象,方便插入和删除操作.
例如,现有两种情况
A: a = [1,3] b = [2,4] a.append(b) b.append(a) del a del b B: a =[1,3] b = [2,4] a.append[b] b.append(a) del a
- Okey,现在开始说正题.在标记清除算法中,有两个集中营,一个是root链表,另一个是unreachable链表
- 对于情景A,原来在未执行DEL语句的时候,a,b的引用计数都为2(init+append=2),但是DEL执行完毕后,a,b引用次数互相减少1.a,b陷入循环引用的圈子,然后标记-清除算法开始出来搞事了,找到其中一段a,开始拆这个a,b的引用环(我们从A出发,因为它有一个对B的引用,则将B的引用计数减1;然后顺着引用到达B,因为B有一个对A的引用,同样将A的引用减1,这样就完成了循环引用对象之间环的摘除),去掉以后发现a,b循环引用变为了0,所以a,b就被处理到unreachable链表中被做掉了.
- 对于情景B,简单一看b去环之后引用计数还为1,但是a取环,就为0了,这时候a已经进入unreachable链表中,已经被判了死刑,但是这个时候,root链表中有b,在root链表中b会被引用检测到引用了a,如果a被回收,b就凉凉了,所以a被拉回到root链表中
- 为什么要搞这两个链表?
- 之所以要剖成两个链表,是基于这样的一种考虑:现在unreachable可能存在被root链表中的对象,直接或间接引用的对象,这些对象不能被回收,一旦在标记的过程中,发现这样的对象,就将其从unreachable链表中移到root链表中,当完成标记后,unreachable链表中剩下的对象就是名副其实的垃圾对象了,接下来的垃圾回收只需要限制在unreachable链表中即可.
分代回收
- 了解分代回收,首先要了解一下GC的阈值,所谓阈值就是一个临界点的值,随着你的程序的运行,Python解释器保持对新创建对象,以及因为引用计数为零而被释放掉的对象的追踪.从理论上来说,创建==释放的数量.但是如果存在引用循环,肯定会导致创建>释放数量,当创建与释放数量的差值到达规定的阈值的时候,分代回收机制就登场了.
- 分代回收思想将对象分为三代(generation 0,1,2),0代表幼年对象,1代表青年对象,2代表老年对象.根据弱代假说(越年轻的对象越容易死掉,老的对象通常会存活更久),新生的对象被放入0代,如果该对象在第0带的一次gc中活了下来,那么它就被放到第1带里里面(它就生就了).如果在第一代的一次gc垃圾回收中活了下来,他就被放到第2代里面gc.set_threshold(threshold0[,threshold1[,threshold2]])设置gc每一代垃圾回收所出发的阈值.从上一代第0代gc后,如果分配对象的个数减去释放的个数大于threshold0,那么就会对第0代中的对象进行gc垃圾回收检查.从上一次第1代gc后,如果第0代被gc垃圾回收的次数大于threshold1,那么就会对第1代的对象进行gc垃圾回收检查,从上一次第2代gc后,如果第一代被gc垃圾回收次数大于threshold2,那么就会对第2代中的对象进行gc垃圾回收检查
以上是关于Python垃圾回收(GC)的主要内容,如果未能解决你的问题,请参考以下文章