python内存管理学习笔记

视频地址:https://www.bilibili.com/video/BV1F54114761?p=6&spm_id_from=pageDriver

Python内存管理

python内存管理:引用计数器为主,标记清除和分代回收为辅 + 缓存机制

引用计数器

环状双向链表refchain

在这里插入图片描述
在python程序中创建的任何对象都会放在refchain链表中。
name = “ wupeiqi ”
age = 18
hoobby = {“ basketball”, “ beauty ”}
内部会创建一些数据【上一个对象,下一个对象、类型、引用个数】
name = “ wupeiqi ”
new = name
内部会创建一些数据【上一个对象,下一个对象、类型、引用个数、val = 18】
age = 18
内部会创建一些数据【上一个对象,下一个对象、类型、引用个数、items = 元素、元素个数】
hobby = {“ basketball”, “ beauty ”}

C源码
在这里插入图片描述
在C源码中如何体现每个对象中都有相同的值:PyObject结构体(4个值:_ob_next _ob_prev ob_refcnt ob_type)
有多个元素组成的对象:PyVarObject结构体(4个值+ob_size)

类型封装结构体

在这里插入图片描述
如上图,每个类型都有一个结构体封装,除了之前的两种(4个值和5个值)的结构体,还添加了相应的参数,举例:
data = 3.14
内部会创建
_ob_next = refchain中的下一个对象
_ob_prev = = refchain中的上一个对象
ob_refcnt = 1
ob_type = float
ob_fval = 3.14

引用计数器

vl = 3.14
v2 = 999
v3 = (1,2,3)
当python程序运行时,会根据数据类型的不同找到其对应的结构体,根据结构体中的字段进行创建,然后将对象添加到refchain中
在C源码中有两个关键的结构体:PyObject PyVarObject
每个对象中有 ob_refcnt ,他就是计数器,值默认为1,当有其他变量引用对象时,引用计数器会发生变化。
举例:
a = 99999 #(ob_refcnt = 1)
b = a #(ob_refcnt = 2)
del b #(ob_refcnt = 1)
del a #(ob_refcnt = 0)

当引用计数器为0,说明不在使用这个对象了,将其归为垃圾,进行回收。
回收就是将对象从refchain中移除;然后将对象销毁,释放内存

引用计数器的bug

v1 = [11,22,33] # refchain中创建一个列表对象,引用计数器为1
v1 = [1,2,3] # refchain中创建一个列表对象,引用计数器为1
v1.append(v2) #用计数器为2
v2.append(v1) #用计数器为2
del v1 #用计数器为1
del v2 #用计数器为1
可以发现,尽管v1 v2已经删除,但是引用计数器仍然为1,会占用内存。

标记清除

目的:为了解决引用计数器循环引用的不足
实现:在python的底层在维护一个链表,链表中专门放那些可能存在循环引用的对象(list/tuple/dict/set)
在这里插入图片描述
在python内部某种情况下触发,会去扫描可能存在循环引用的链表中的每个元素,检查是否有循环引用,如果有则让双方的引用计数器各自减1,如果是0进行垃圾回收。
问题:

  1. 什么时候扫描?
  2. 可能存在循环引用的链表扫描代价大,耗时久
    在这里插入图片描述
    转自:vhttps://blog.csdn.net/weixin_39931923/article/details/111428919

分代回收

在这里插入图片描述
将可能存在循环引用的对象分成三个链表:

  1. 0代:0代中对象个数达到700个扫描一次
  2. 1代:0代扫描10次,则1代扫描一次
  3. 2代:1代扫描10次,则2代扫描一次
    举例:
    创建一个list,将对象加入refchain中,同时加到0代中,当0代满700后进行扫描,将垃圾回收,不是垃圾的对象放到1代,以此类推。

总结

  1. 在python中维护了一个refchain双向环形链表,这个链表中存储着创建的所有对象,每种类型的对象都有一个ob_refcnt引用计数器,引用个数会随对象引用次数增减,最后当引用计数器为0时进行垃圾回收(对象销毁,回归内存)
  2. 但是在python中,对于那些由多个元素组成的对象可能会存在循环引用的问题,为了解决这个问题,python引入了标记清除和分代回收,在内部维护了4个链表:refchain、0代、1代2代
  3. 在源码内部,当达到各自阈值时,就会触发扫描,进行标记清除

优化机制:python缓存

池(int/string等)

为了避免重复创建和销毁一些常见对象,会维护一个池,举例:
启动解释器时,python内部帮我们创建:-5、-4、…、257等数字,
v1 = 7 #内部不会开辟内存,直接去池中获取
v2 = 9 #内部不会开辟内存,直接去池中获取
v3 = 9
print(id(v3),id(v2)) # v2 v3都来自于小数据池,输出地址相同

free_list(float/list/tuple/dict)

当一个对象的引用计数器为0,理论上应该回收,但在python内部不会回收它,而是将其加入free_list中当做缓存,以后在创建这个对象时,不在重新开辟内存,而是直接使用free_list,举例:
v1 = 3.14 # 开辟内存,创建对象
del v1 # 将v1从refchain中移除,讲对象添加到free_list
v9 = 99.9 # 不回重新创建内存开辟内存,去free_list中获取对象,对象内部数据初始化,再放到refchain中。缓冲池满了再进行销毁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值