Python使用的垃圾回收方式是引用计数(reference counting)和垃圾回收器(garbage collector), 在2.0版本之前只有引用计数,2.0版本之后带有garbage collector,并且默认开启,可以通过gc模块来进行控制。
Python通过引用计数来管理已分配的内存,引用计数是一个简单的内存管理方式,它的一个缺陷就是循环引用(reference cycles)的问题, 这种情况下多个对象的计数都不会为0,且其他对象无法访问其中的对象,形成一个“孤岛”。实例代码如下:
# 主动删除对象
l = []
l.append(l)
del l
# 变量离开作用域
def make_cycle():
l = [ ]
l.append(l)
make_cycle()
循环引用是一个比较难在实践中完全避免的问题,对于长时间运行的服务端程序来说是非常不利的。 为了解决循环引用的问题,需要额外的开销来发现内存中的循环引用并进行回收,通常这类开销都比较大, 会造成停顿,gc的代价较高。
在Python中,循环引用只会出现在container对象中,例如list、dictionary、tuple、class、instance等。
在Python的实现中,设置有一个threshold,作为垃圾回收器的触发条件。 设已分配的对象数量为allocated,释放的对象数量为freed,当满足allocated - freed > threshold的时候, 会启动垃圾回收器,这个threshold有可能会一直没达到,并且和物理内存的大小无关。
可以通过gc模块来获取threshold,代码如下
import gc
print gc.get_threshold() # (700, 10, 10)
你应该注意到这里的threshold是返回了三个值,后面会介绍Python内部对内存进行了分代(generations)管理的,分别叫作0、1、2,现在先忽略。
当程序内存耗尽(Out of Memory)的时候,自动垃圾回收器(Automatic Garbage Collection)是不会运行的,而是Python解释器会抛出异常MemoryError,可以捕获该异常并进行处理。
垃圾回收的耗时是依赖要释放的对象的个数,而不是要释放对象的大小。基于这个原因,在工程实践中有几点建议:
- 循环引用不是一种好的编程实践,在编写代码的时候,应该尽量避免
- 在程序中如果一次性释放大量的对象,最好手动执行一次gc(调用gc.collect())
- 对于长时间运行的服务程序,最好可以在适当的时机调用一次gc,使得循环引用越少越好(可以定时调度或者在某些服务器空闲的时间段进行调度)