从 CPython 源码角度看 Python 垃圾回收机制
环状双向链表 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 时,意味着没有人再使用这个对象了,这个对象就会被垃圾回收,流程如下:
- 把对象从 refchain 链表中移除
- 将对象销毁,内存归还
注:del
语句实际上就是在对引用计数器做 -1
操作。
循环引用
在 Python 底层,会维护一个新的链表,用于存放可能存在循环引用的对象(如 list/dict/set/tuple等)。当达到一定条件后,会去遍历每个元素,检查是否有循环引用,如果有,则让双方的引用计数 -1,如果是 0 则进行回收。
分代回收
循环引用引发了两个问题:
- 什么时候扫描?
- 扫描代价较大(对子孙元素都要进行扫描),单词扫描耗时久。
对此,Python 使用了分代回收的机制。将可能存在循环引用的对象维护成 3 个链表:
- 0 代,个数达到 700 个扫描一次
- 1 代,0 代扫描 10 次,1 代扫描 1 次
- 2 代,1 代扫描 10 次,2 代扫描 1 次
缓存
池(int)
为了避免重复的创建和销毁一些对象,维护池。
>>> a1 = 1
>>> a2 = 1
>>> id(a1)
140713557615440
>>> id(a2)
140713557615440
free_list(float/list/tuple/dict)
当一个对象的引用计数为 0 时,内部不会直接回收,而是将对象添加到 free_list 中当缓存,以后再去创建对象时,不再重新开辟内存,而是直接使用 free_list。
# 开辟新的内存
v1 = 3.14 # 将对象添加到 free_list 中
del v1 # 去 free_list 中获取对象,并将对象内存数据初始化
v2 = 999
参考
从 CPython 源码角度看 Python 垃圾回收机制的更多相关文章
- 简述Python垃圾回收机制和常量池的验证
目录 通过代码验证python解释器内部使用了常量池 Python的引入 变量的引入 为什么要有变量 定义变量 常量引入 常量池引入 Python解释器 Python变量存储机制 Python垃圾回收 ...
- 从JDK源码角度看Short
概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...
- 从JDK源码角度看Byte
Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...
- 从JDK源码角度看Object
Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类.它包含了对象常用的一些方法,比如getClass.hashCode.equals.clone. ...
- 从JDK源码角度看Boolean
Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换. 主要实现源码如下: public fi ...
- python垃圾回收机制:引用计数 VS js垃圾回收机制:标记清除
js垃圾回收机制:标记清除 Js具有自动垃圾回收机制.垃圾收集器会按照固定的时间间隔周期性的执行. JS中最常见的垃圾回收方式是标记清除. 工作原理 当变量进入环境时,将这个变量标记为"进入 ...
- python垃圾回收机制与小整数池
python垃圾回收机制 当引用计数为0时,python会删除这个值. 引用计数 x = 10 y = x del x print(y) 10 引用计数+1,引用计数+1,引用计数-1,此时引用计数为 ...
- 浅析Python垃圾回收机制!
Python垃圾回收机制 目录 Python垃圾回收机制 1. 内存泄露 2. Python什么时候启动垃圾回收机制? 2.1 计数引用 2.2 循环引用 问题:引用计数是0是启动垃圾回收的充要条件吗 ...
- 从template到DOM(Vue.js源码角度看内部运行机制)
写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些 ...
随机推荐
- RocketMQ源码详解 | Broker篇 · 其四:事务消息、批量消息、延迟消息
概述 在上文中,我们讨论了消费者对于消息拉取的实现,对于 RocketMQ 这个黑盒的心脏部分,我们顺着消息的发送流程已经将其剖析了大半部分.本章我们不妨乘胜追击,接着讨论各种不同的消息的原理与实现. ...
- 第一章 初始C语言
第一章 初始C语言 目录 第一章 初始C语言 1. C语言起源 2. 选择C语言的理由 2.1 设计特性 2.2 高效性 2.3 可移植性 2.4 强大而灵活 2.5 面向程序员 3. C语言的应用范 ...
- 洛谷 P3285 - [SCOI2014]方伯伯的OJ(平衡树)
洛谷题面传送门 在酒店写的,刚了一整晚终于调出来了-- 首先考虑当 \(n\) 比较小(\(10^5\) 级别)的时候怎么解决,我们考虑将所有用户按排名为关键字建立二叉排序树,我们同时再用一个 map ...
- Codeforces 650D - Zip-line(树状数组)
Codeforces 题目传送门 & 洛谷题目传送门 我怕不是个 nt--一开始忽略了"询问独立"这个条件--然后就一直在想有什么办法维护全局 LIS--心态爆炸 首先离散 ...
- Atcoder Regular Contest 123 题解
u1s1 我是真的不知道为什么现场这么多人切了 D,感觉 D 对思维要求显然要高于其他 300+ 人切掉的 D 吧(也有可能是 Atc 用户整体水平提升了?) A 开 幕 雷 击(这题似乎 wjz 交 ...
- 【豆科基因组】绿豆Mungbean, Vigna radiata基因组2014NC
目录 来源 一.简介 二.结果 基因组组装 重复序列和转座子 基因组特征和基因注释 绿豆的驯化 豆科基因组复制历史 基于转录组分析的豇豆属形成 绿豆育种基因组资源 三.讨论 四.方法 材料 组装 SN ...
- LVS-原理
一. 集群的概念 服务器集群简称集群是一种服务器系统,它通过一组松散集成的服务器软件和/或硬件连接起来高度紧密地协作完成计算工作.在某种意义上,他们可以被看作是一台服务器.集群系统中的单个服务器通常称 ...
- Oracle基础入门
说明:钓鱼君昨天在网上找到一份oracle项目实战的文档,粗略看了一下大致内容,感觉自己很多知识不够扎实,便跟着文档敲了一遍,目前除了机械性代码没有实现外,主要涉及知识:创建表空间.创建用户.给用户赋 ...
- 【模板】Splay(伸展树)普通平衡树(数据加强版)/洛谷P6136
题目链接 https://www.luogu.com.cn/problem/P6136 题目大意 需要写一种数据结构,来维护一些非负整数( \(int\) 范围内)的升序序列,其中需要提供以下操作: ...
- 11 — springboot集成swagger — 更新完毕
1.前言 理论知识滤过,自行百度百科swagger是什么 2.导入依赖 <!-- swagger所需要的依赖--> <dependency> <groupId>io ...