python中的垃圾回收机制是以引用计数器为主,标记清除和分代回收为辅的 + 缓存机制
1.引用计数器
在python内部维护了一个名为refchain的环状双向链表,在python中创建的任何对象都会放在refchain中
name = "kunmzhao" age=18
在python中创建的对象,在底层是由C语言中的结构体存储的
在这些结构体中存储这上一个对象,下一个对象,对象类型和引用个数
1.1 引用计数
在python运行过程中,当我们创建一个对象的时候,会根据数据类型找到对应的结构体,创建对象,然后将对象添加在refchain中,并且引用计数为1.当对象被引用的时候就会+1,当对象被del的时候就-1
当一个对象的引用计数=0的时候,代表着这个对象就是垃圾,需要被回收,将对象从refchain中移除
1.2 循环引用问题
当两个列表互相嵌套的时候,就出现了循环引用,不解决的话就出现了内存泄漏
2.标记清除
标记清除的目的是为了解决引用计数中的循环引用问题
实现原理:在python内部再维护一个双线链表,用于存放可能存在循环引用的对象(列表,字典,元组,集合类型),在一定条件下出触发链表的全部扫描,检查是否有循环引用,如果存在,则将双方的引用计数-1,如果为0则进行垃圾及回收
3.分代回收
目的是为了优化标记清除,因为如果标记清除维护的链表很大,每次都遍历会非常消耗性能,为此分代回收维护了3个链表,3个链表代表着不同的扫描阈值,当达到阈值之后才会触发对应链表的扫描
0代:0代中对象的个数达到700个则扫描一次
1代:0代扫描10次,则1代扫描一次
2代:1代扫描10次,则2代扫描一次
将可能存在循环引用的对象放在0代的链表中,当0代中的对象个数超过700个就会触发一次链表扫描,将引用个数为0的回收,不为0的提升到1代链表中,并将0代扫描次数设置为1,当0代扫描次数为10次的时,就会触发1代进行扫描,将引用个数为0的进行回收,不为0的提升为2代,并将1代扫描次数设置为1
小结:在python中,维护了一个refchain的双向链表,存放着python中创建的所有对象,每个对象都有一个ob_refcnt的引用计数器,变量被引用一次,则个数+1,删除一次,个数-1,当个数为0,则进行垃圾回收,但是由于循环引用的问题,python引入了标记清除和分代回收,又维护了3个链表,0代,1代和2代,代表着不同的阈值,当各自达到对应的阈值就会触发扫描链表,进行标记清除
4.缓存机制
频繁的申请和销毁内存是非常消耗时间的,为此又引入了缓存机制,包括缓存池和free_list
4.1缓存池
池的做法是预先缓存一些常见的对象比如-5到256的整型对象,和ASCII字符表等。当程序中定义了一个小的整型对象时,不需要从内存中申请内存,直接从池中获取即可
4.2 free_list
free_list的做法是将程序本来删除的对象,没有直接销毁而是放在一个名为free_list的数组中。后面当程序中重新创建一个相同类型的对象时,不必新开辟空间而是直接从free_list中获取对象,重新进行初始化并放入到refchain中去。
class Foo(object): name = 'kunmzhao' def __init__(self, name): self.name = name def send_msg(self): print(self.name) foo = Foo('kunmzhao') # 2324775989200 print(id(foo)) del foo foo1 = Foo('Victor') # 2324775989200 print(id(foo1))
标签:
留言评论