目录
垃圾回收作用
垃圾回收机制原理
概述
引用计数机制
优点
缺点:
标记-清除
实现
标记阶段
清除阶段
分代回收
垃圾回收作用
垃圾回收机制可以回收不使用的变量值所占用的空间,释放空间,达到空间重复使用的目的
程序运行过程中会申请大量的内存空间,但当程序运行时并不是所有空间都可以被使用,如果不及时回收内存空间会导致内存空间用尽(内存溢出),导致程序崩溃,垃圾回收机制就是用来解决这一问题。
垃圾回收机制原理
概述
Python 的 GC(Garbage Collection) 模块主要运用了引用计数来跟踪和回收垃圾;通过“标记-清除”解决容器对象可能产生的循环引用问题;通过分代回收以空间换时间进一步提高垃圾回收的效率
引用计数为主(实时性,一旦没有引用,内存就直接释放了), 标记-清楚与 分代收集两种机制为辅的策略
引用计数机制
python 中一切皆对象(包括变量等),所有对象都是放在一个双向环状循环链表 refchain 上,对象核心是一个结构体 PyObject
。当引用计数为 0 的时候,该对象便被回收。变量值被关联次数的增加或减少,都会引发引用计数机制的执行。
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
typedef struct _object{
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
} PyObject;
导致引用计数 +1 的情况:
- 对象被创建,例如
a = 23
- 对象被引用,例如
b = a
- 对象被作为参数,传入到一个函数中,例如
func(a)
- 对象作为一个元素,存储在容器中,例如
l1 = [a, a]
导致引用计数 -1 的情况:
- 对象的别名被显式销毁,例如
del a
- 对象的别名被赋予新的对象,例如
a = 24
- 一个对象离开它的作用域,例如函数执行完毕时,函数中的局部变量
- 对象所在的容器被销毁,或从容器中删除对象
可以通过 sys 包中的 getrefcount() 来获取一个名称所引用的对象当前的引用计数器的值,但该函数会使得引用计数器 +1
回收过程:1. 对象从 refchain 链表移除。2. 将对象销毁,内存归还
优点
- 简单
- 实时性:一旦没有引用,内存就直接释放了,不用等到特定时机,处理回收内存的时间分摊到了平时。
缺点:
- 维护引用计数消耗资源
- 循环引用 (交叉引用)
list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,所占用的内存永远无法被回收,这将是致命的.
标记-清除
解决循环引用的不足与可能导致内存泄漏
实现
在 python 底层维护一个链表,用来存放可能存在循环引用的对象(一般为容器,如 list, tuple, dict, set)
在Python内部某种情况下触发,会去扫描可能存在循环引用的链表中的每个元素,检查是否有循环引用,如果有则让双方的引用计数器-1;如果是0则垃圾回收。
该算法分为两个阶段:标记阶段、清除阶段
标记阶段
遍历所有的对象,如果还有对象引用他(可达),就标记该对象为活动对象,不可达的对象就是需要清除的非活动对象
清除阶段
再次遍历对象,如果发现某个对象没有标记为可达,这就将其回收
分代回收
根据对象的生存周期判断该对象是否可能是垃圾,生存期较短对象的比例通常在 80%~90% 之间。因此,简单地认为:对象存在时间越长,越可能不是垃圾,应该越少去收集。这样在执行标记-清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度,是一种以空间换时间的方法策略。
Python 将所有的对象分为 0,1,2 三代
所有的新建对象都是 0 代对象
当某一代对象经历过垃圾回收,依然存活,就被归入到下一代对象