Python内存回收原理

概述

引用计数为主,标记清除和分代回收为辅构成了python的内存回收机制.

1. 引用计数

1.1 环状双向链表 refchain

python中创建的任何对象都会加到refchain链表中.
每个对象都会有一个结构体

typedef struct _object { struct _object * ob_next; strcut _object * ob_prev; int ob_refcnt; struct_typeobject *ob_type; } PyObject;
这个结构体里有上一个对象的指向和下一个对象的指向,类型,引用个数.
l另外一个结构体
typedef struct {
Pyobject ob_base;
Py_ssize_t ob_szie; //items 的个数
}PyVarobject ; 这个主要用于列表,元组等内部含有多个元素的结构对象.

1.2类型封装的结构体

//当创建一个在这里float对象时
typedef struct{
	PyObject ob_base;
	double ob_fval;
}PyFloatObject;

//其他几种类型也是类似的.

1.3引用计数器

当python程序运行时,会根据数据类型不同找到其相应的结构,根据结构体中字段来进行创建相关数据的数据,然后将对象添加到refchain双向链表中.
在C源码中有两个关键的结构体, PyObject PyVarObject
每个对象都有ob_refcnt 就是引用计数器,默认值是1,当有其他变量引用对象时,引用计数器就会发生变化.
a=9999 b=a 那么 9999这个对象的引用计数就会变成2
删除引用 del b 这个时候b变量删除,b对应对象引用计数减一
当一个对象的引用计数器为0,意味着没有人再使用这个对象了,这个对象就是垃圾,垃圾回收
回收: 1对象从refchain链表移除, 2,将对象销毁,内存归还.

2.标记清除

2.1 循环引用的问题
v1=[1,2,3]
v2=[4,5,6]
v1.append(v2)
v2.append(v1)
del v1
del v2
这时会产生循环引用的问题

为了解决这个问题,python底层又创建一个链表,链表种存放着可能存在循环引用的元素如list等。在python内部某种情况下会触发扫描遍历整个链表里的每个元素,检查是否有循环引用,如果有就让双方引用计数减一,如果是0就垃圾回收。
而又有两个问题:
1.什么时候扫描
2.可能存在循环引用的链表扫描代价大,每次扫描耗时久

就需要下面的分代回收

3.分代回收:

将可能存在循环引用的对象维护成3个链表.
0代: 0代中的对象个数达到700个扫描一次
1代 : 0代扫描10次,则1代扫描一次
2代 1代扫描10次,则2代扫描一次
都是双向循环链表.
先是把可能存在循环引用的元素放到0代中,当0代达到700个的时候,会进行少少秒,如果计数为0的则回收,否则加到1代中,这样依次类推

小结

在python中维护了一个refchain双向循环链表,这个链表中存储程序创建的所有对象,每种类型的对象中都有一个ob_refcnt引用计数器的值,引用个数+1,-1
最后当引用计数器变为0的时候会进行垃圾回收,(对象销毁,从refchain中进行移除)但是,python中对于那些有多个元素组成的对象可能会存在循环引用的问题,为了解决这个问题python又引入了标记清除和分代回收,在器内部为4个链表.
refchain 2代 1代 0代
在源码内部当达到各自的阈值时,就会触发扫描链表进行标记清除的动作(有循环则会各自-1)
python内部又在上述问题中提出了优化机制.

python缓存
池:
为了避免重复的区创建和一些常见的对象,维护池.
启动解释器 python内部帮我们创建了 -5 … 257
free_list :
当一个对象的引用计数为0, 按理说因该回收,内部不会直接回收,而是将对象添加到一free_list链表中当缓存,以后再区创建对象时,不会重新开辟内存,而是直接使用free_list

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值