python 中的垃圾回收之标记清除

Python引入了其他的垃圾收集机制来弥补引用计数的缺陷:"标记-清除","分代回收"两种收集技术.


标记-清除


"标记-清除"是为了解决循环引用的问题.可以包含其他对象引用的容器对象(比如:list,set,dict,class,instance)都


可能产生循环引用.


我们必须承认一个事实,如果两个对象的引用计数都为1,但是仅仅存在他们之间的循环引用,那么这两个对象都是


需要被回收的,也就是说,它们的引用计数虽然表现为非0,但实际上有效的引用计数为0.我们必须先将循环引用摘


掉,那么这两个对象的有效计数就现身了.假设两个对象为A、B,我们从A出发,因为它有一个对B的引用,则将B的引


用计数减1;然后顺着引用达到B,因为B有一个对A的引用,同样将A的引用减1,这样,就完成了循环引用对象间环摘

除.

先看一组代码:

#第一组循环引用#

a = [1,2]

b = [3,4]

a.append(b)

b.append(a)

del a 

#第二组循环引用#

c = [4,5]

d = [5,6]

c.append(d)

d.append(c)

del c

del d

#至此,原a和原c和原d所引用的对象的引用计数都为1,b所引用的对象的引用计数为2,

e [7,8]

del e

现在说明一下标记清除:代码运行到上面这块了,此时,我们的本意是想清除掉c和d和e所引用的对象,而保留a和b所引用的对象。但是c和d所引用对象的引用计数都是非零,原来的简单的方法只能清除掉e,c和d所引用对象目前还在内存中。

假设,此时我们预先设定的周期时间到了,此时该标记清除大显身手了。他的任务就是,在a,b,c,d四个可变对象中,找出真正需要清理的c和d,而保留a和b。

首先,他先划分出两拨,一拨叫root object(存活组),一拨叫unreachable(死亡组)。然后,他把各个对象的引用计数复制出来,对这个副本进行引用环的摘除。摘除完毕,此时a的引用计数的副本是0,b的引用计数的副本是1,c和d的引用计数的副本都是0。那么先把副本为非0的放到存活组,副本为0的打入死亡组。如果就这样结束的话,就错杀了a了,因为b还要用,我们把a所引用的对象在内存中清除了b还能用吗?显然还得在审一遍,别把无辜的人也给杀了,于是他就在存活组里,对每个对象都分析一遍,由于目前存活组只有b,那么他只对b分析,因为b要存活,所以b里的元素也要存活,于是在b中就发现了原a所指向的对象,于是就把他从死亡组中解救出来。至此,进过了一审和二审,最终把所有的任然在死亡组中的对象通通杀掉,而root object继续存活。b所指向的对象引用计数任然是2,原a所指向的对象的引用计数仍然是1









猜你喜欢

转载自blog.csdn.net/master_ning/article/details/80784656
今日推荐