01 垃圾回收机制
一、什么是垃圾回收机制?
- 垃圾回收机制检测GC,是python解释器自带的一种机制,专门用来回收不可用得变量值所占用得内存空间。
二、为什么要有垃圾回收机制?
- 程序得运行过程中会申请大量的内存空间,而对于一些无用的内存空间如果不及时清理的话,会导致内存使用殆尽(也就是内存溢出),从而导致程序奔溃。因为管理内存是一件复杂且繁琐的事情,而python解释器自带的垃圾回收机制把程序员从复杂的内存管理中解放出来。
三、垃圾回收机制原理分析
1、什么是引用计数?
- 运用了“引用计数”(reference counting)来跟踪和回收垃圾。
- 引用计数就是变量值被变量名关联的次数。
1.1 直接引用
- 通过变量名直接引用
x = 10 # 值10直接引用:1次
y = x # 值10直接引用:2次
z = y # 值10直接引用:3次
print(id(x)) # 140715024107456
print(id(y)) # 140715024107456
print(id(z)) # 140715024107456
1.2 间接引用
- 主要争对容器类型
c = 10
li = ['a', 'b', c] # 列表相当于['a', 'b', 10],值10被间接引用:1次
print(id(c)) # 140715024107456
print(id(li[-1])) # 140715024107456, 值10被间接引用:2次
dic = {'key': c} # 值10被间接引用:3次
print(id(dic['key'])) # 140715024107456, 值10被间接引用:4次
1.3 由引用关系刨析列表取值的底层原理过程
- 我们知道列表是由所以取值,且列表能存多个任意类型的值,当我们通过列表取值时对应的正真的就是屏幕上我们所看到的值吗?我们来看图分析:
- 更具上图分析,列表的取值操作,我们是由索引对应值的内存地址,再通过这个内存地址找到对应值所在内存中的正真位置,列表当中相当于存放了内存地址的目录,通过这个目录我们访问到正真值所在的位置。
1.4 总结
- 只要是访问到你那个值得方式,就相当于增加一次引用。
- 只要是直接通过变量名访问值得方式,都是直接引用。
- 间接引用一般是通过容器类型来进行对值得访问,例如:列表,字典类型等
- 列表中的值并不是正真的值,而是通过内存地址映射到值得位置。
2、引用计数扩展阅读
2.1 标记清除
- 通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用(也交交叉引用)的问题。
- 循环引用时,值不再被任何名字关联,但是值的引用计数并不会为0,因该被回收,但不能被回收,这个时候就需要python的标记清除功能,清除没有被直接引用的值。
- 标记清除程序并不是时时检测的,只有内存快满了,python程序才会启动标记清除程序,扫描栈区,如果谁身上没有直接引用的标识,它将根据对应内存地址去清除堆区的正真值占用的内存。
- 栈区:存放变量名对应值得内存地址
- 堆区:存放对应内存地址得值
li1 = [10]
li2 = [20]
li1.append(li2) # li1 = [值10对应得内存地址, 列表2对应得内存地址],此时列表li1得引用计数为2
li2.append(li1) # li2 = [值20对应得内存地址,列表1对应额内存地址],此时列表li2得引用计数为2
del li1
del li2
# 通过del解除变量名li1与值[10]得绑定关系,变量名li2与值[20]的绑定关系,虽然解绑了直接引用,此时li1与li2得间接引用关系没有断,任然互相关联着,也就是说li1与li2值当前引用计数为1,但是它们并不能被访问到了,这就导致了它们一直占用了这块内存空间,这对于计算机来说是致命得。
# 总结:循环引用时,值不再被任何名字关联,但是值的引用计数并不会为0,因该被回收,但不能被回收。
总结
- 标记清除是用来解决循环引用带来得内存泄漏问题。
2.2 分代回收
- 通过“分代回收”(generation collection)以空间换取时间的方式来进一步提高垃圾回收的效率。
- 详细内容地址:https://zhuanlan.zhihu.com/p/108683483
总结
- 用来降低引用计数的扫描频率,提升垃圾回收的效率。