python垃圾回收机制(参考自https://sutune.me/)
- 概述
- python采用的是引用计数为主,分代回收 和 标记清除 两种机制为辅的策略
- 一、引用计数
- python默认采用“引用计数法Reference Counting”
- “引用计数法”原理:每个对象维护一个ob_ref字段,用来记录此对象被引用的次数
- 当有新的引用指向时,引用计数+1
- 当该对象的引用计数失效时,引用计数 -1
- 一旦对象的引用计数为0,该对象立即被回收,空间将被释放
- 引用计数案例:
-
- 引用计数的漏洞
- 让我们思考,假如,当我们产生了一种建立引用次数大于可删除引用次数的时候,就会导致这些空间永远不会被释放,那么这个时候就产生了内存泄漏。这种方式我们称之为“循环引用”
- 循环引用案例:如下图所示,当我们在内部产生两个循环调用的时候,就算删除掉外部的两个引用,这两个实例都不会被回收机制回收,因为他们仍然存在引用
- 引用计数的漏洞
- 二、分代回收
- python将内存根据对象的存活时间分为不同的集合,每个集合称为一代,python将内存分为了3代。分别为 年轻代(第0代)、中年代(第1代)、老年代(第2代)
- 这三代分别对应的是三个链表
- 新创建的对象会被分配在年轻代。年轻代链表总数达到上限的时候,python的垃圾回收机制就会触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去。以此类推,老年代中的对象是存活时间最久的对象
- 分代回收建立在标记清除的技术基础上
- 三、三种触发垃圾回收的情况
- 调用gc.collect(),需要先导入gc模块
- 当gc模块的计数器达到阈值的时候
- 程序退出时
- 四、gc模块
- gc模块可以解决循环引用的问题,常用函数如下
- gc.collect([generation])
- gc模块可以解决循环引用的问题,常用函数如下
-
-
-
- 垃圾回收。输入参数0代表只检查第一代,输入参数1代表检查第一代和第二代,输入参数2代表检查第一、二、三代,不传的时候默认为2.
- 返回不可达对象的数目
- gc.get_count()
-
-
-
-
-
- 获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表
-
- gc模块自动垃圾回收机制
- 在import gc之后,设置is_enable()=True才会启动自动垃圾回收
- 此外还有一点需要注意
- 如果在循环引用中,两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,所以为了安全起见,gc模块会把对象放在gc.garbage中(这家伙是个列表)
-
- 五、标记清除
- 标记清除是一种基于追踪回收的算法,分两个阶段(下图4和5是两个非活动对象)
-
-
- 第一阶段,从跟对象出发,沿着有向边遍历对象,可达的对象标记为“活动对象”,不可达的对象就是要被清除的”非活动对象“
- 第二阶段,把那些“非活动对象”进行回收
- 标记清除主要处理一些容器对象,比如list、dict、tuple、instance等
- 算法缺陷:操作时必须顺序扫描整个堆内存
-