目录

垃圾回收作用

垃圾回收机制原理

概述

引用计数机制

优点

缺点:

标记-清除

实现

标记阶段

清除阶段

分代回收


垃圾回收作用

垃圾回收机制可以回收不使用的变量值所占用的空间,释放空间,达到空间重复使用的目的

程序运行过程中会申请大量的内存空间,但当程序运行时并不是所有空间都可以被使用,如果不及时回收内存空间会导致内存空间用尽(内存溢出),导致程序崩溃,垃圾回收机制就是用来解决这一问题。

垃圾回收机制原理

概述

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 代对象

当某一代对象经历过垃圾回收,依然存活,就被归入到下一代对象