python的垃圾回收机制

python的垃圾回收机制

一,引用计数

python中的垃圾回收机制主要采用引用计数的方式来跟踪和回收垃圾;

from sys import getrefcount
a = [1, 2, 3]
b = a
c = [4, a]

del b
# 相当于给a的引用计数-1
c.remove(a)
# del a
# 只是让a的引用计数为0,并没有删除[1, 2, 3]这块内存空间(内存的删除是由底层c语言实现的)

# print(getrefcount(a))  # 这里a当作参数,也是一次引用
# 引用计数增加的情况
# 赋值
# 引用
# 当成函数参数传递给函数


# 引用被显性或者隐性的删除
# 函数结束的时候里面参数的引用计数会减一

# 当这个对象的引用计数为0的时候 我们就认为它是垃圾可以被回收

优点:1.简单 2.实时性
缺点:1.维护引用计数消耗资源 2.循环引用

二, 标记-清除

光使用引用技术解决不了容器对象可能产生的循环引用问题.例如:

a = [1,2,3,4]
b = [5,6,7]
a.append(b)
b.append(a)  # a, b循环引用

print(getrefcount(a))
print(getrefcount(b))

所以,python在引用计数的基础上,使用标记清除来解决循环引用的问题:

以全局变量出发, 可以找到所有可达对象,所有不可达对象就是垃圾,只要是可达对象,就不会被回收;

局部变量,如在函数内部,只有在函数执行过程中才会创建内存,函数执行完之后就可以回收了;

缺点:效率问题,每次都要遍历所有的对象,效率很慢

三,分代回收

'分代回收'以空间换时间的方法提高垃圾回收效率。 在内存中存活时间越长 越不可能是垃圾;

一共有3代:

0代 最年轻的一代
当0代对象达到一个标准的时候我们就去触发垃圾回收,从而触发引用计数,在去做标记清除;
然后把存活的0代放入1代,当我们0代触发10次的时候 就触发1代的回收;
1代经过被回收之后,存活的对象放入2代,当1代触发10次 触发2代回收(本次就要遍历剩下所有的2代,标记清除了);

这个标准就是:

import gc
print(gc.get_threshold())  # (700, 10, 10)

这个(700, 10, 10)代表这个标准,都是可以改变的,其中700代表 调用c接口开辟内存跟销毁内存的次数差值,10分别代表0代触发10次回收1代和1代触发10次回收2代。

补充:

python中对于操作内存分为2大块:

  • python自己维护内存池(小数据池就是在这个空间中)
  • 调用c的接口 去开辟内存空间

总结:

  • 引用计数
    • Python为每个对象维护一个引用计数
    •  当引用计数为0的 代表这个对象为垃圾
  • 标记清除
    • 解决孤立的循环引用
    • 标记根节点和可达对象
    • 不可达视为垃圾
  • 分代回收
    • 解决标记清除的效率问题
    • 0代 1代 2代
    • 阈值 (700,10,10)
    • 当调用c的接口开辟内存和销毁内存的差值为700的时候出发0代回收
    • 0代触发10次 触发1代回收
    • 1代触发10次 触发2代回收
    • 每次回收结束 没有被回收的对象放入下一代

 

posted @ 2018-09-25 17:38  Mr.GLH  阅读(290)  评论(0)    收藏  举报
Top↑