0%

python | 引用计数器

python 的内存管理机制是引用计数器为主,标记清除和分代回收为辅 + 缓存机制。

环状双向链表 refchain

python 中创建的任何一个对象都会放在 refchain 中。

当创建一个对象,比如

1
name = "123"

会同时创建

  • 上一个对象
  • 下一个对象
    • 链表结构
  • 类型
  • 引用个数

当然,根据类型的不同,具体的存储也有一点不同,比如,对于 数组来说,会增加

  • items 属性
  • 元素个数

对于 int 或者其他来说,会增加

  • value 属性

C 语言源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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;

typedef struct{
PyObject ob_base; //PyObject 对象
Py_ssize_t ob_size; // 元素个数
}PyVarObject;

对于不同的类型,会创建不同的结构体

float 类型

1
2
3
4
typedef struct{
PyObject_HEAD
double ob_fval;
}PyFloatObject;
  • 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。当有其他变量引用的时候,引用计数器会发生变化。

1
2
3
a = 999 # ob_refcnt = 1
b = a # ob_refcnt = 2
del a # ob_refcnt = 1

当引用计数器为 0 时,无对象使用,则会进行垃圾回收。

请我喝杯咖啡吧~