【Python】垃圾回收机制

文章目录


背景

无论是什么语言的程序,在其运行期间都需要在内存中开辟一块空间,用于存放运行时所产生的临时变量(Temporary);在其运行完成之后,需将结果存储到硬盘中。
当程序运行过程之中,数据量过大、或内存管理不善等情况,很容易出现内存泄露,程序可能会被操作系统异常终止。
→说明:由于程序设计问题,导致未能释放不再使用的内存,所导致的内存浪费,称之为内存泄露。

  • 引用计数器
  • 标记清除
  • 分代回收

refchain

在 Python 中,所有对象都存储在 refchain 的双向链表中。	
#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; 			// Number of items in variable part,即:元素个数
} PyVarObject;
ob_refcnt

引用计数器


标记清除


分代回收


在refchain中所有对象有有一个ob_refcnt,用来保存当前对象的引用计数器(即:被引用的次数)。
不考虑缓存机制的情况下
当创建对象时,会在内存中创建
当引用对象时,不会直接在内存中创建数据,而是进行“引用计数器+1”操作;
当销毁对象时,不会直接在内存中销毁数据,而是进行“引用计数器-1“操作。
如果引用计数器为0
有缓存情况下,则不会销毁对象
无缓存情况下,将对象从refchain链表中删除,同时在内存中销毁
如果引用计数器不为0,则不进行操作。

当对象被引用时,不会在内存中重复创建数据,而是进行“引用计数器+1”操作
当对象被销毁时,同样的不会直接在内存中销毁数据,而是进行“引用计数器-1”操作

如果引用计数器为0,那么将对象从refchain链表中删除,同时在内存中销毁(除缓存特殊情况除外)

标记清除/分代回收
如果对象存在循环引用的情况下,单靠引用计数器时无法正常进行垃圾回收的
会导致无法正常的回收一些垃圾

为了解决循环引用问题,引入了标记清除的技术——专门用于可能存在循环引用的对象进行特殊处理,
可能存在的类型有:list、tuple、dict、set、自定义类等那些能够进行数据嵌套的类型

标记清除:
创建特殊的链表,专门用于保存列表、元祖、字典、集合、自定义类等对象,之后再去check这个链表中是否存在循环引用
如果有,则进行“引用计数器-1”操作
如果没有,则无操作

分代回收
对标记清除中的链表进行优化,将那些可能存在循环引用的对象拆分到3个链表中,
0/1/2代链表
每一代链表,都可以存储对象和阈值
当到达阈值时,则会对对应的链表中的每一个对象进行扫描,除循环引用各自-1并且销毁引用计数器为0的对象
0代,count表示0代链表中对象的数量,threshold表示0代链表对象个数阈值,超过则执行一次0代扫描检查。
1代,count表示0代链表扫描的次数,threshold表示0代链表扫描的次数阈值,超过则执行一次1代扫描检查。
2代,count表示1代链表扫描的次数,threshold表示1代链表扫描的次数阈值,超过则执行一2代扫描检查。

缓存机制

上一篇:Windows内核开发-10-监听对象


下一篇:JS判断是否为空的方法