环状双向链表 refchain

在 Python 程序中创建的任何对象都会被放到 refchain 链表中,当创建一个 Python 对象时,内部实际上创建了一些基本的数据:

  • 上一个对象
  • 下一个对象
  • 类型
  • 引用个数
  • 对于列表等类型,也会创建值用于存储列表的长度

在 C 源码中体现如下:

#define PyObject_HEAD		PyObject ob_base;
#define PyObject_VAR_HEAD PyVarObject ob_base; // 宏定义,包含:上一个、下一个、用于构造双向链表用
#define _PyObject_HEAD_EXTRA
struct _object *_ob_next;
struct _object *_ob_prev; typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt; // 引用计数器
struct _typeobject *ob_type; // 数据类型
} PyObject; // list、tuple、dict..
typedef struct {
PyObject ob_base; // PyObject 对象
Py_ssize_t ob_size; // 元素个数
} PyVarObject; // float
typedef struct {
PyObject_HEAD
double ob_fval;
} PyFloatObject;

比如对于下面这段 Python 代码:

data = 3.14

其内部会创建:

	_ob_next = refchain 中的下一个对象
_ob_prev = refchain 中的下一个对象
ob_refcnt = 1
ob_type = float
ob_fval = 3.14

引用计数器

在 Python 程序运行时,会根据数据类型的不同找到其对应的结构体,根据结构体中的字段来进行创建相关的数据,然后将对象添加到 refchain 双向链表中。

在 C 源码中有两个关键的结构体:PyObject、PyVarObject。

每个对象中都有 ob_refcnt,即引用计数器,默认值为 1,当有其他变量引用对象时,引用计数器就会发生变化。

当一个对象的引用计数器为 0 时,意味着没有人再使用这个对象了,这个对象就会被垃圾回收,流程如下:

  1. 把对象从 refchain 链表中移除
  2. 将对象销毁,内存归还

注:del 语句实际上就是在对引用计数器做 -1 操作。

循环引用

在 Python 底层,会维护一个新的链表,用于存放可能存在循环引用的对象(如 list/dict/set/tuple等)。当达到一定条件后,会去遍历每个元素,检查是否有循环引用,如果有,则让双方的引用计数 -1,如果是 0 则进行回收。

分代回收

循环引用引发了两个问题:

  • 什么时候扫描?
  • 扫描代价较大(对子孙元素都要进行扫描),单词扫描耗时久。

对此,Python 使用了分代回收的机制。将可能存在循环引用的对象维护成 3 个链表:

  • 0 代,个数达到 700 个扫描一次
  • 1 代,0 代扫描 10 次,1 代扫描 1 次
  • 2 代,1 代扫描 10 次,2 代扫描 1 次

缓存

  1. 池(int)

    为了避免重复的创建和销毁一些对象,维护池。

    >>> a1 = 1
    >>> a2 = 1
    >>> id(a1)
    140713557615440
    >>> id(a2)
    140713557615440
  2. free_list(float/list/tuple/dict)

    当一个对象的引用计数为 0 时,内部不会直接回收,而是将对象添加到 free_list 中当缓存,以后再去创建对象时,不再重新开辟内存,而是直接使用 free_list。

    # 开辟新的内存
    v1 = 3.14 # 将对象添加到 free_list 中
    del v1 # 去 free_list 中获取对象,并将对象内存数据初始化
    v2 = 999

参考

  1. python 垃圾回收机制刨析

从 CPython 源码角度看 Python 垃圾回收机制的更多相关文章

  1. 简述Python垃圾回收机制和常量池的验证

    目录 通过代码验证python解释器内部使用了常量池 Python的引入 变量的引入 为什么要有变量 定义变量 常量引入 常量池引入 Python解释器 Python变量存储机制 Python垃圾回收 ...

  2. 从JDK源码角度看Short

    概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...

  3. 从JDK源码角度看Byte

    Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...

  4. 从JDK源码角度看Object

    Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类.它包含了对象常用的一些方法,比如getClass.hashCode.equals.clone. ...

  5. 从JDK源码角度看Boolean

    Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换. 主要实现源码如下: public fi ...

  6. python垃圾回收机制:引用计数 VS js垃圾回收机制:标记清除

    js垃圾回收机制:标记清除 Js具有自动垃圾回收机制.垃圾收集器会按照固定的时间间隔周期性的执行. JS中最常见的垃圾回收方式是标记清除. 工作原理 当变量进入环境时,将这个变量标记为"进入 ...

  7. python垃圾回收机制与小整数池

    python垃圾回收机制 当引用计数为0时,python会删除这个值. 引用计数 x = 10 y = x del x print(y) 10 引用计数+1,引用计数+1,引用计数-1,此时引用计数为 ...

  8. 浅析Python垃圾回收机制!

    Python垃圾回收机制 目录 Python垃圾回收机制 1. 内存泄露 2. Python什么时候启动垃圾回收机制? 2.1 计数引用 2.2 循环引用 问题:引用计数是0是启动垃圾回收的充要条件吗 ...

  9. 从template到DOM(Vue.js源码角度看内部运行机制)

    写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些 ...

随机推荐

  1. Duboo整合SpringBoot超级详细例子(附源码)

    dubbo3.0整合SpringBoot例子 dubbo新版本(3.0以上)在相对于 dubbo 旧版本(2.5.2.6.2.7),有很多的不相同的地方. 官方文档也说了新版本的特性: https:/ ...

  2. [tc14634]ExtremeSpanningTrees

    保序回归论文题 要求某一个边集为原图的最小生成树,这等价于非树边比所在环(指树上)的所有边小,最大生成树类似 将这些大小关系的限制看作一张有向图,即若要求$w_{i}\le w_{j}$则连边$(i, ...

  3. [loj3048]异或粽子

    先对其求出前缀异或和,然后$o(k)$次枚举,每次选择最大值,考虑如何维护可以全局开一个堆,维护出每一个点的最大值的最大值,那么相当于要在一个点中删去一个点再找到最大值将这些删去的点重新建成一颗tri ...

  4. 【Linux】(1)安装

    VMware虚拟机安装Linux,IP地址显示为127.0.0.1的解决方案 ① 打开该虚拟机,点击导航栏"虚拟机(M)",选择"设置(S)..." ② 将&q ...

  5. 全面了解 Javascript Prototype Chain 原型链

    原型链可以说是Javascript的核心特征之一,当然也是难点之一.学过其它面向对象的编程语言后再学习Javascript多少会感到有些迷惑.虽然Javascript也可以说是面向对象的语言,但是其实 ...

  6. Haywire

    还是模拟退火乱搞. 不过考虑记录一下在整个退火过程中的最优答案. 而不是只看最后剩下的解. 退火是一个随机算法,他有很大的几率能跳到最优解,但也很有可能从最优解跳出去. 所以要记录答案. Haywir ...

  7. Topcoder 15405 - PrettyLiar(可删除背包+前缀和优化 dp)

    题面传送门 题意: 给出两个长度为 \(n\) 的数组 \(a,b\) 和一个整数 \(s\). 你可以任意重排数组 \(a,b\),总共 \((n!)^2\) 种方案. 现在又两个人 A,B 来玩游 ...

  8. 【宏组学】如何根据taxid(或taxname)快速获得taxname(或taxid)?

    需求 我有一个物种taxonomy ID的list,想获得相应的物种名,不要一个个去NCBI Taxonomy官网查.反之根据物种名list查询对应的taxid. 实现 因为之前没怎么用过,我的第一个 ...

  9. Oracle-创建新表,创建备份表,对表中插入多条数据

    一.创建新表 0.基本语法 create table 表名称(id varchar2(50) primary key ,name char(200) not null,phone number(11) ...

  10. 进程和线程操作系统转载的Mark一下

    https://www.cnblogs.com/leisure_chn/p/10393707.html Linux的进程线程及调度 本文为宋宝华<Linux的进程.线程以及调度>学习笔记. ...