python垃圾回收
-
引用计数
-
环状双向列表(refchain)
在python中,任意对象都会放在双向列表里面
name = 'dream' age = 18 happy = ['Computer Game','Play']
""" 存放的相同的数据 在内部创建一个结构体 (打包成一个数据) 存放的数据: 上一个数据的指针 下一个数据的指针 数据的类型 引用的个数 """ # age = 18 """ 存放你的数据 不同的: val = 18 """ # happy = ['Computer Game','study'] """ 存放的数据 item = 存放的元素 元素个数 """
- 在C源码中PyObject体现出每个对象都有相同的值
- 由多个元素组成的的值:PyObject + ob_size :
-
类封装结构体
a = 3.14 """ 内部会创建 _ob_next 环状双向列表存放的上一个数据 _ob_prev 环状双向列表存放的下一个数据 ob_refcnt = 1 引用个数 ob_type = Float 对象类型 ob_val = 3.14 对象的值 """
-
引用计数器
v1 = 3.14 v2 = 999 v3 = (1,2,3)
在python程序运行时,会根据数据类型的不同找到其对应的结构,根据结构中的字段来创建相关的数据,然后添加到双向列表中。在C源码中有两个结构体比较重要:PyObject,PyVarObject
PyObject:存放的是工共的数据。比如说,上一个数据的指针,下一个数据的指针………
PyVarOjbect :存放的是多个元素组成的时候,公共的值。
每个对象在创建的过程中,都有一个ob_refcnt(引用计数):默认是1,当有其他变量引用对象时,引用计数器就会发生变化。
- 引用
a = 999 b = a # 此时999的引用就会变成2了
-
删除引用
a = 999 b = a del b # 把对象b删除,此时b对象的引用计数器减一 # 把对象a删除,此时a对象的引用计数器减一 # 当对象的引用计数器为0时,意味着没有使用这个对象了,这个对象就是垃圾,就会进行垃圾回收 # 回收:把对象从refchain进行回收,将这个对象从内存销毁,并把内存归还
-
-
循环引用
a = ['a','b','c','d'] b = ['a','b','c','d'] a.append(b) # 把列表b添加列表a的末尾 同时b的引用计数器+1 b.append(a) # 把列表a添加列表b的末尾 同时a的引用计数器+1 del a # 删除列表a 同时a的引用计数器-1 del b # 删除列表b 同时b的引用计数器-1 """ 此时a,b的引用计数都为1 """
- 标记清除
- 目的:为了解决循环引用
- 实现:在python在去维护一个列表,列表中存放一个可能出现循环引用的对象。
- 可能出现循环引用的对象
- 列表
- 字典
- 元组
- 在python情况下***在某种情况下***触发,回去扫描可能出现***循环引用列表***中的每个元素。检查是否有循环引用。如果是则双方的引用计数减一,如果是0则进行垃圾回收。
- 问题
- 什么时候扫描
- 代表比较大(比较耗时)
- 标记清除
-
分代回收
将可能出现循环引用的对象维护成三个链表
- 0代 :0代中的对象达到700个扫描一次
- 1代 :0代扫描十次,则1代开始扫描
- 2代 :1代扫描十次,则2代开始扫描
v1 = [1,2,3,4,5] v2 = [6,7,8,9,0] v1.append(v2) v2.append(v1) """ 第一步:会把v1和v2放在0代的链表里面。 第二部:当0代的链表里面达到700个数据时会进行扫描。 第三步:查看里面是否有循环引用的数据,如果有就会把引用次数-1。 第四步:查看引用次数,如果引用次数为0,就会被回收,如如果不为零就会把里面所有的数据给1代 """
-
小节
在python中自己维护了一个refchain双向维护列表,这个链表中存储程序创建的所有对象。每个对象都有一个ob_frecnt引用计数的值。引用计数+1或者-1。当引用计数为0时,就会进行垃圾回收(对象销毁,从refchain移除)
但是,对于python中可以有多个元素可能出现循环引用的问题。为了解决这个问题。python引入了标记清除,和分代回收。在内部建了四个链表
- refchain (python自己的环状双向链表)
- 2代:第一代扫描十次
- 1代:第零代扫描十次
- 0代:有700个数据
当源码内部达到各自的阈值后,就会触发扫描标记清除动作。
源码内部上述提出了优化机制(缓存机制)
-
缓存机制
-
池(int)
目的:为了避免重复创建和销毁对象、维护chi
# 启动解释器时 Python内部会自动帮我们创建-4~256 v1 = 7 # 不会创建一个对象,会直接从池中进行获取 v2 = 9 # 不会创建一个对象,会直接从池中进行获取 v3 = 9 # 不会创建一个对象,会直接从池中进行获取 # 此时 v2和v3的id地址是一样的 v4 = 5555 # 会创建一个对象 v5 = 666 # 会创建一个对象 v6 = 666 # 会创建一个对象 # 此时v5和v6的地址是不一样的
-
free_list(tuple、list、float、dict)
a = 3.14 # 创建一个Float的对象 del a # 不会进行销毁,经对象添加到free_list当中,当free_list满了,才会进行销毁 a = 34.434 # 当第二次进行创建对象的时候不会直接创建对象,而是从free_list中获取到a,然后进行初始化,然后在放到refchain当中
a = (1,3,4) """ 元组中的free_list free_list = [0,1,2,3,4,5,6,7,8........19] 元组中的元素个数为0是,放在free_list中的0号里面 元组中的元素为个数1时,放在free_list中的1号里面 ...... 元组中的元素个数为19,放在free_list中的19号里面 """ tuple1 = (1,2) del tuple tuple2 = (3,4) # 不会直接销毁元组,而是把他放在free_list的2号里面,然后进行初始化值
维护
unicode_latin1[256]
链表,内部将所有的ascii字符
缓存起来,以后使用时就不再反复创建。str1 = 'niaho' del a str2 = 'niaho' # 此时str1和str2的内存地址是一样的 # 除此之外,Python内部还对字符串做了驻留机制,针对那么只含有字母、数字、下划线的字符串(见源码Objects/codeobject.c),如果内存中已存在则不会重新在创建而是使用原来的地址里(不会像free_list那样一直在内存存活,只有内存中有才能被重复利用)。
-