1、垃圾回收机制详解
1.1 引用计数
引用计数表示的是一个变量被引用的次数
例如:
x = 10 # 直接引用,10的引用计数变为1 print(id(x)) y = x # 直接引用,10的引用计数变为2 z = x # 直接引用,10的引用计数变为3 print(id(x),id(y),id(z))
这里提到了直接引用的概念,和它相对的还有间接引用.
如果x在容器类型(如列表和字典)中被使用即10被间接引用了,间接引用同样会增加引用计数
x = 10 # 直接引用,10的引用计数变为1 l = ['a', 'b', x] # 间接引用,10的引用计数变为2 print(id(l[2])) d = {'mmm': x} # 间接引用,10的引用计数变为3 print(id(d['mmm']))
引用的显著特点即他们的id都相同
在python中会自动将引用计数为0的变量值清除,回收其占有的内存空间,防止内存占用过高,这就是垃圾回收机制
1.2 标记清除
标记清除是一种用来解决循环引用时产生内存泄漏,即出现无法清除的变量值的方法.
lis1=[1,2,3] lis2 = [2,3] lis1.append(lis2) # 此时lis1能间接引用lis2 lis2.append(lis1) # 此时lis2能间接引用lis1 print(lis1) print(lis2) del lis1 # 解除变量名lis1和变量值的关系 del lis2 # 解除变量名lis2和变量值的关系 # 此时lis1里的数据与lis2里的数据都无法通过直接或间接引用的方法得到 # 但是之前列表内数据的引用计数不为0,他们还有间接引用的关系,无法直接被垃圾回收机制清除
此时我们必须引入新的方法,即标记清除
我们知道,变量名存放在栈中,变量值存放在堆内,python会在内存将满时使用标记清除,检测现存放在栈中的所有变量名,并对其标记为存活,接着进入堆内检测是否有和存活变量名完全无关的数据,一旦发 现就将它清除,这就是标记清除.
可以以一个简单易于理解的例子来解释:栈内的变量名就像树的根,堆内的变量值就像树的枝干,一旦一 棵树根全部被清除,树就无法存活,这时就需要将没有根的枝干全部清除.
1.3 分代回收
了解了python的垃圾回收机制,我们就要考虑一个问题,那就是回收垃圾的频率问题,如果无时无刻对所有的变量值进行检测,会占用大量的内存,我们需要尽量降低python的扫描频率,提高垃圾回收的效率,python中使用了分代回收来解决这一难题.
分代回收就是将变量值进行分代,分成使用频率不同的三代:新生代,中年代,老年代,
新变量都为新生代,在过程中动态检测变量值,扫描频率新生代最高,老年代最低,扫描多次依旧存活的变量会进入中年代或者老年代,减少部分变量值的扫描次数,以此达到提高效率,降低占用内存的目的.