python的垃圾回收机制
python的垃圾回收机制:应用计数为主,标记清除和分代回收为辅
一.引用计数
这里1这个对象并没有在内存中新建,因为python有代码块机制,在python解释器启动的时候会将-5~正无穷的数字加载到内存中等待调用,a=1只是为1添加了一个引用。
import sys a = 1 print(sys.getrefcount(a)) #1366
再来看一个例子
a = 23345455 #引用次数1 b = a #2次 c = b #3次 print(sys.getrefcount(b)) #4 调用getrefcount()会再加一次,因此打印出来为4次 print(sys.getrefcount(c)) #4
得到结果为4,4.因为查看引用并非查看b或c对象被引用的次数,而是查看23345455这个值的被引用次数,此时删除c变量,23345455这个值的被引用次数-1,直到为0,就会被回收。
二.循环引用
通过引用计数器的方式基本上可以完成Python的垃圾回收,但它还是具有明显的缺陷,即:“循环引用” 。
import gc import objgraph class Foo(object): def __init__(self): self.data = None # 在内存创建两个对象,即:引用计数器值都是1 obj1 = Foo() obj2 = Foo() # 两个对象循环引用,导致内存中对象的应用+1,即:引用计数器值都是2 obj1.data = obj2 #将obj2赋值给obj1.data,obj2指向Foo(),因此Foo()的引用为2 obj2.data = obj1 #同上 # 删除变量,并将引用计数器-1。 del obj1 del obj2 # 关闭垃圾回收机制 gc.disable() # 至此,由于循环引用导致内存中创建的obj1和obj2两个对象引用计数器不为0,无法被垃圾回收机制回收。 # 所以,内存中Foo类的对象就还显示有2个。 print(objgraph.count('Foo'))
循环引用的问题会引发内存中的对象一直无法释放,从而内存逐渐增大,最终导致内存泄露。
为了解决循环引用的问题,Python又在引用计数器的基础上引入了标记清除和分代回收的机制。
三.标记清除和分代回收
Python为了解决循环引用,每创建一个对象都会将对象放到一个双向链表中,每个对象中都有 _ob_next 和 _ob_prev 指针
1.当对象个数超过 700个 时,Python解释器就会进行垃圾回收,然后代码中主动执行 gc.collect() 命令时,Python解释器就会进行垃圾回收。
2.Python解释器在垃圾回收时,会遍历链表中的每个对象,如果存在循环引用,就将存在循环引用的对象的引用计数器 -1,同时Python解释器也会将计数器等于0(可回收)和不等于0(不可回收)的一分为二,把计数器等于0的所有对象进行回收,把计数器不为0的对象放到另外一个双向链表表(即:分代回收的下一代)
3.python里一共有三代,每个代的threshold值表示该代最多容纳对象的个数。默认情况下,当0代超过700,或1,2代超过10,垃圾回收机制将触发。
4.0代触发将清理所有三代,1代触发会清理1,2代,2代触发后只会清理自己。