垃圾回收&&内存管理
引用计数器
Python它的内部有一个ref_chain环状双向列表,我们的对象都存在这个环状双向列表里面,这个环状双向链表主要保存
- 上一个对象和下一个对象
- 对象的类型,例如字符形等等
- 引用计数器,
ob_refcnt
- 这个对象的值
- 如果对象是列表,元组等,会保存他们的元素,以及个数‘
环状双向链表保存的计数器,当我们使用del
删除一个对象的时候,它的值就减一,如果计数器是0了,就会被回收。当我们多次引用一个变量的时候,它的值会加一,而不是重复的创建。
但是如果单单只靠这个链表保存的计数器ob_refcnt
还不行,因为多个元素组成的对象,比如列表,元组可能会有循环引用的问题。
比如我有A,B两个列表,他们刚开始引用计数器都是1,如果我操作,A.append(B),那么B的引用计数器会加一,也就是变成2,然后如果在操作del B,那么B理应被清除,因为它没有对象引用,但是他的引用计数器这时候是1,不是0,所以不会被回收。
当这种循环引用的情况越来越多的时候,就会导致我们内存越来越慢,最后可能重启才能恢复。
标记清除
为了解决这问题,Python有标记清除和分代回收机制。在Python底层还有一种链表,它专门存那些可能会引起循环引用的对象,比如列表,元组。Python会去扫描这个链表,看看这里面的对象有没有循环引用,如果有,就把计数器减一.
这个扫描可能很耗时,怎么扫描呢?
分代回收
Python有分代回收机制
分代回收机制有3个链表,也就是分成3代,分别是0代,1代和2代。当达到阈值的时候才扫描。
当对象达到700个,才扫描0代链表
当0代链表扫描了10次,才扫描1代链表。
当1代链表扫描了10次,才扫描2代链表
过程:
把可能造成循环引用的对象先放到0代,当0代链表的对象超过700个就去扫描一次,扫描看看里面的对象有没有循环引用,有循环引用的对象计数器就减一,如果最后计数器是0了,就回收。如果扫描到的对象没有循环引用,就把它加到1代链表里面去。
Python的缓存机制
引用计数器为0时,不会真正销毁对象,而是将他放到一个名为 free_list
的链表中,之后再创建对象时不会在重新开辟内存,而是去free_list
中获取对象,然后初始化数据,再加入到refchain
中
但是每种数据类型它的缓存机制可能不太一样。
-
float类型,维护的free_list链表最多可缓存100个float对象。
-
list类型,维护的free_list数组最多可缓存80个list对象。
-
dict类型,维护的free_list数组最多也是可缓存80个dict对象,和list类似
-
元组类型
,特殊,元组类型维护一个20个元素的free_list,free_list里面的元素存的是索引,第一个元素,用来放只有一个元素的元组,一共可以放2000个。第二个元素,用来放2个元素的元组,也是一共可以放2000个,以此类推。 -
也就是说:元组的free_list数组在存储数据时,是按照元组可以容纳的个数为索引找到free_list数组中对应的链表,并添加到链表中。
-
int类型和字符型不是使用free_list,而是使用缓存池
-
int类型维护一个
small_ints
链表保存常见数据(小数据池),小数据池范围:-5 <= value < 257。即:重复使用这个范围的整数时,不会重新开辟内存。>>> a='c' >>> id(a) 140674715138808 >>> b='c' >>> id(b) 140674715138808
-
字符型维护一个
unicode_latin1[256]
链表,内部将所有的ascii字符缓存起来,以后使用时就不再反复创建。下图可以看到用的同一个内存地址>>> v1='a' >>> v2='a' >>> id(v1) 140674715137248 >>> id(v2) 140674715137248
-
除此之外,Python内部还对字符串做了驻留机制,针对那么只含有字母、数字、下划线的字符串(见源码Objects/codeobject.c),如果内存中已存在则不会重新在创建而是使用原来的地址里
>>> v1='suzijian' >>> v2='suzijian' >>> id(v1) 140674479927920 >>> id(v2) 140674479927920
池:Python为了避免太频繁的创建值,它有一个池,这个池是启动解释器的时候自动帮我们创建的,比如解释器一启动,就会有一个池,里面放着-5,-4一直到256,这些值是Python认为最常用的数字,当我们去执行a=1,b=1的时候,不会去开辟内存,而是直接使用这个池里边的那个1,把它加到ref_chain里面。所以他们的内存地址也会一模一样,这个池里面的数据不会被回收,这样就减少了很多不必要的创建值和销毁值。