python内存管理&垃圾回收
Posted pyliuwei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python内存管理&垃圾回收相关的知识,希望对你有一定的参考价值。
python内存管理&垃圾回收
引用计数器
环装双向列表refchain
在python程序中创建的任何对象都会放在refchain连表中
name = ‘张三‘ age = 18 hobby = [‘汽车‘,‘游艇‘]
创建一个变量!内部会创建一些数据【上一个对象,下一个对象,类型,引用个数
name = ‘张三‘ 内部会创建一些数据【上一个对象,下一个对象,类型,引用计数】 age = 18 内部会创建一些数据【上一个对象、下一个对象、类型、引用计数、val=18】 hobby = [‘汽车‘,‘游艇‘] 内部会创建一些数据【上一个对象、下一个对象、类型、引用计数、items=元素】
#define Pyobject_HEAD
#define Pyobject_VAR_HEAD
//宏定义,包含 上一个、下一个,用于构造双向连表用(放到refchain链表中,要用到)
#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//数据类型
}PrObject;
typedef struct {
PyObject ob_base;//PyObject对象
Py_ssize_t ob_size;//Number of items in variable part 元素个数
}PyVarObject;
在C源码中如何体验每个对象都有相同的值:PyObject结构体(4个值)
有多个元素组成的对象:PyObject结构体(4个值)+ob_size
类型封装结构体
data = 3.14
内部会创建:
_ob_next refchain中的上一个对象
_ob_prev refchain中的下一个对象
_ob_refcnt = 1
_ob_type = float
_ob_fval = 3.14
引用计数器
v1 = 3.14
v2 = 999
v3 = (1,2,3)
当python程序运行时,会根据数据类型的 不同找到其对应类型的结构体,根据结构体中的字段来创建相关的数据,然后将对象添加到refchain双向链表中。
在C源码中有两个关键的结构体:PyObject、PyVarObject
每个对象这种有ob_refcnt就是引用计数器,值 默认为1,当有其他变量引用对象时,引用计数器就会发生变化。
#引用
a = 666
b = a 引用计数2
a = 666
b = a
del b #b变量删除:b对应对象引用计数器-1
当一个对象的引用计数器为0时,意味着没有使用这个对象,这个对象就是垃圾。会被垃圾回收。
#回收:1.当对象从refchain链表移除,2.将对象销毁。内存归还。
引用计数器的BUG
循环引用的问题
标记清除
目的:为了解决引用计数器循环引用的不足
实现:在python的底层,维护一个链表,链表中专门放可能存在循环引用的对象【tuple、list、dict、set】。
在python内部某种情况
下触发,回去扫描 可能存在循环应用的链表中的每个元素,检查是否有循环引用,如果有引用,让双方的引用技术器-1,如果是0 则垃圾回收。
问题:什么时候扫描?
可能存在循环引用的链表扫码代价大!每次扫描耗时时间长。
分代回收
将可能存在循环引用的对象维护成3个链表
- 0代:0代中对象个数达到700个扫描一次。
- 1代:0代扫描10次,则一代扫描1次。
- 2代:1代扫描10次,则2代扫描1次。
小结
在python中维护了一个refchain的双向环装链表,这个链表中存储程序创建的所有对象,每种没醒的对象都有一个_obj_refcnt引用计数器的值,引用个数+1、-1,最后当引用计数器变为0时会进行垃圾回收(对象销毁、refchain中移除).
但是,在python对于那些可以有多个元素组成的对象可能会存在循环引用的问题,为了解决这个问题python又引入了标记清除和分代回收,在其内部都为4个链表。
- refchain
- 2代
- 1代
- 0代
在源码中当达到各自的阈值时,,就会触发扫描器进行标记清除的动作(有循环各自-1)
但是,源码内部在上述的流程中提出了优化机制。
python缓存
池
为了避免重复创建和销毁一些常见对象、维护池。
v1 = 7
v2 = 3
v3 = 3
为了节省资源、python只会创建两个资源
池的范围在 -5、 -4 到257
当数据大于257的时候,python会单独在内存中创建资源。
池(int、字符串)
free_list
当一个对象的引用计数器为0时,按道理讲应该会回收,但是在python中不会直接回收,而是将对象添加到free_list链表中当缓存,以后再去创建对象时,不再重新开辟内存,而是直接使用free_list.
v1 = 3.14 开辟内存,内部存储结构中定义那几个值,并存到refchain中 del v1 refchain中移除,将对象添加到free_list中【80个】,free_list满了才销毁。 v9 = 9999 不会重新开辟内存,去free_list中获取对象,对象内部数据初始化,再放到refchain中
以上是关于python内存管理&垃圾回收的主要内容,如果未能解决你的问题,请参考以下文章
[新星计划] Python内存管理 | 引用计数垃圾回收内存池机制