垃圾回收机制
Python的垃圾回收机制是以引用计数机制为主,标记清除和分代收集两种机制为辅的策略所实现的
-
引用计数机制
之前说过,每个对象创建后,都有一个引用计数。当某个对象的引用计数为0的时候,垃圾回收机制会对这个对象进行销毁,并回收内存空间 - 图解(这里的1和2应该改成非小整数机制的数值才合理,因为小整数机制的数值的引用计数不会变成0,一直被小整数机制池引用中)
-
引用计数存在一种缺点:当两个对象出现互相引用的时候,那么这两个对象/变量始终不会被垃圾回收机制所销毁,这样会导致内存泄漏
1.比如:
2.下面这种情况,就算del 删掉l1和l2,只是删掉了列表的表层,里面的元素删除不了,因为它们在互相引用中。所以导致内存中的l1和l2也不会被删除释放,因为它们的引用计数不为0,会造成内存泄漏
标记清除
1.标记清除是为了弥补引用计数的缺点而设计的。它会判断一个对象是否有被全局变量所引用,如果没有,则对这个对象进行标记,然后进行删除并回收内存。
2.比如说上面的“列表中的元素互相引用”的情况,在del l1
和del l2
之后,单纯使用引用计数无法删除“1”和“2”这两个对象(应该写非小整数池的数字更准确)。而标记清除这时候就会去顺着引用进行去查找,看“1”和“2”是否被全局变量所引用(直接或间接的引用都判断),如果没有,则给它们打上标记,然后清除掉它们并回收内存空间。分代回收
分代回收策略主要是用来提高垃圾回收的效率。为了避免频繁地去对对象的遍历和销毁回收,导致性能
1.创建对象的时候,会把这些对象放到一个链表中(一代链表),等这个链表中的对象达到700个时(默认700),就去遍历这个链表中的对象的引用计数。把引用计数为0的对象给删掉,然后不为0的对象,放入到第二个链表中(二代链表)。
2.当一代链表被清除了10次后,再去二代链表中再次执行在一代链表的操作,把引用计数不为0的对象,放入到第三个链表中(三代链表)
3.当二代链表被清楚了10次后,再去三代链表再次执行在一代链表的操作。但是这次引用计数不为0的对象,依旧会放在三代链表中
gc模块
- 一般用来查看分代回收的三代链表的回收阙值(条件)
gc.get_threshold()
: 获取分代回收的阙值 - 也可以修改设置这些阙值,不过一般不会去设置
gc.set_threshold(一代条件, 二代条件, 三代条件)
:设置分点回收的阙值
import gc
# 获取分代回收的阙值:
id1 = gc.get_threshold()
gc.set_threshold(1000, 20, 30) # 设置分点回收的阙值
id2 = gc.get_threshold()
print(id1) # (700, 10, 10):结果分别是一代、二代、三代
print(id2) # (1000, 20, 30)