简介
Python 中垃圾回收,以引用计数为主,标记清除和分代为辅。其中,标记清除解决引用计数里循环引用无法清除的问题,分代回收用于提升垃圾回收的效率。
引用计数
引用计数:记录对象被其他使用对象引用的次数,使用 sys.getrefcount() 获取对象的引用计数
引用计数增加:
- 一个对象分配给一个新的名字(a=[1,2])
- 将其放入一个容器内([].append(a))
引用计数减少:
- del a 显示删除对象
- 对象所在容器被销毁或者从容器中删除对象
- 引用超出作用域或重新赋值
标记-清除
标记-清除用来解决引用计数机制产生的循环引用,进而导致内存泄漏的问题 。 循环引用只有在容器对象才会产生,比如字典,元组,列表等。
标记阶段:
遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达
清除阶段:
再次遍历对象,如果发现某个对象没有标记为可达(即为Unreachable),则就将其回收
分代回收
Python将所有的对象分为年轻代(第0代)、中年代(第1代)、老年代(第2代)三代。所有的新建对象默认是 第0代对象。当在第0代的gc扫描中存活下来的对象将被移至第1代,在第1代的gc扫描中存活下来的对象将被移至第2代。
当某一代中被分配的对象与被释放的对象之差达到某一阈值时,就会触发当前一代的gc扫描。当某一代被扫描时,比它年轻的一代也会被扫描,因此,第2代的gc扫描发生时,第0,1代的gc扫描也会发生,即为全代扫描。
>>> import gc
>>> gc.get_threshold() ## 分代回收机制的参数阈值设置
(700, 10, 10)
- 700=新分配的对象数量-释放的对象数量,第0代gc扫描被触发
- 第一个10:第0代gc扫描发生10次,则第1代的gc扫描被触发
- 第二个10:第1代的gc扫描发生10次,则第2代的gc扫描被触发