GC in Python

Python垃圾回收处理

引用计数

当一个对象被创建或者复制时,对象的引用计数加一,当对象的引用被销毁时,对象的引用计数减一,当引用计数为0时,会将该对象所占内存释放。

优点在于实时性,任何内存一旦没有指向它的引用,就会立即被回收,其它垃圾回收机制是建立在一些特殊条件下。

缺点在于维护计数所造成的计算开销。同时,引用计数会造成循环引用,如下所示

1
2
3
4
5
6
7
8
a = []
b = []
a.append(b)
b.append(b)
print(a)
[[[…]]]
print(b)
[[[…]]]

循环引用会使一组对象的引用计数不为0,但这些对象实际上并没有被任何外部对象所引用,只是它们之间相互引用,没人使用却平白增加了引用,所占内存永远不会被释放。为了解决这个问题,python引入了“标记-清除”和“分代回收”两种方式

标记-清除

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

当A引用了B,B却没有引用A,B符合释放条件时被释放后,A对于B的引用就会为空,所以我们为了维护A对B的引用,就要在释放B的时候检查引用B的对象,会造成巨大的计算开销,所以我们采取的方案是不改动真是引用计数,将集合中对象的引用计数复制一份副本,改动引用副本,这个副本的作用是寻找根对象集合,所有没有引用却引用了别的对象的对象集合,然后遍历非根对象集合中的对象,一旦存在被根对象直接或间接引用,则这样的对象肯定不能删除,反之,则加入考虑是否删除的集合中,最后只需要维护这个集合就可以了。

分代回收

将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。