python3内存管理和垃圾回收机制原理

334 阅读3分钟

#python3垃圾回收
#引用计数为主,标记清除和分代回收为辅
"""
环状双向链表 refchain
内部创建一些数据(上一个对象,下一个对象,类型,引用个数,等等)
在C语言代码中如何体现每个对象都有相同的值:PyObject结构体(4个值)
有多个元素组成的对象:PyObject结构体(4个值) + ob_size
"""


#内部创建一些数据(上一个对象,下一个对象,类型,引用个数)
name = "peter"

#内部创建一些数据(上一个对象,下一个对象,类型,引用个数,value=22)
age = 22

#内部创建一些数据(上一个对象,下一个对象,类型,引用个数,items=元素,元素个数等)
hobby = ["篮球","咖啡"]

#类型封装结构体
#内部会创建_ob_next,_ob_prev,ob_refcnt = 1,ob_type = float,ob_fval = 3.14
data = 3.14

1636535561444_55A8A780-4DD4-437c-BC4D-B687D60C8E8B.png

1. """引用计数器"""
v1 = 3.14
v2 = 999
v3 = (1,2,3)
当python程序运行时,会根据数据类型的不同找到其对应的结构体,根据结构体中的字段来进行创建相关的数据
然后将对象添加到refchain双向链表中
在C语言源码中有两个关键的结构体:PyObject,PyVarObject
每个对象中都有ob_refcnt就是引用计数器,值默认为1,当有其他变量引用对象时,引用计数器会发生变化


#引用
a = 9999
b = a

#删除引用
a = 9999
b = a
del b   #b变量删除时,对应的对象引用计数-1

"""
当一个对象的引用计数器为0时,意味着没有人使用这个对象,这个对象就是垃圾,垃圾回收
回收:1. 对象从refchain链表移除;2. 将其对象销毁,内存归还
缺点:1. 维护引用计数需要消耗一定资源;2. 循环引用时无法回收
"""


2. 标记清除:为了解决引用计数器循环引用的不足
实现:在python的底层再维护一个链表,链表中专门存放那些可能存在循环引用的对象(list/tuple/dict/set)
在python内部,某种情况下触发,会去扫描可能存在循环引用的链表中的每个元素,如果有则让双方的引用计数器-1
;如果时0则垃圾回收


3. 存在问题:1. 什么时候扫描;2. 可能存在循环引用的链表扫描代价大耗时久

分代回收:将可能存在循环引用的对象维护成3个链表
0代:0代中的对象个数达到700个扫描一次
1代:0代扫描10次,则1代扫描一次
2代:1代扫描10次,则2代扫描一次
"""

"""
python缓存:
1). 池(int类型)
为了避免重复创建和销毁一些常见对象,维护池
启动python解释器时,python内部帮我们创建-5,-4,.....257
2). free_list
当一个对象引用计数为0时,按理应该回收,内部不会直接回收而是将对象添加到free_list链表中当缓存
,以后再创建对象时,不再重新开辟内存,而是直接使用free_list
"""
v1 = 3.14 #开辟内存,内部存储结构体中定义几个值,并存到rechain中

del v1    #rechain中移除,将对象添加到free_list中,free_list满了才销毁

v2 = 99.99 #不会重新开辟新内存,去free_list中获取对象,对象内部数据初始化再放到rechain中