引用计数与垃圾回收 #################################### 所有对象都有引用计数。给一个对象分配一个新名称,或是将其放入一个容器(如列表、元组或字典),都会增加该对象的引用计数,例如: .. highlight:: none :: a = 37    # 创建一个值为 37 的对象 b = a     # 增加 37 的引用计数 c = [] c.append(b)  # 增加 37 的引用计数 这个例子创建了一个包含值 37 的对象,a 只是引用这个新创建的对象的一个名称。将 a 赋值给 b 时,b 就成了同一对象的新名称,该对象的引用计数因此增加。类似地,将 b 放到一个列表中时,该对象的引用计数将再次增加。在这个例子中,至始至终只有一个包含 37 的对象,所有其他操作都只是创建了对该对象的新引用。 使用 del 语句或者引用超出作用域(或者被重新赋值)时,对象的引用计数就会减少,例如: :: del a    # 减少 37 的引用计数 b = 42   # 减少 37 的引用计数 c[0] = 2.0 # 减少 37 的引用计数 使用 sys.getrefcount() 函数可以获得对象的当前引用计数,例如: :: >>> import sys >>> a = 37 >>> sys.getrefcount(a) 7 >>> 多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会主动在程序的不同部分共享对象,以便节约内存。 当一个对象的引用计数归零时,它将被垃圾回收机制处理掉。但在某些情况下,在很多已不再使用的对象间可能存在循环依赖关系,例如: :: a = { } b = { } a['b'] = b  # a 包含对 b 的引用 b['a'] = a  # b 包含对 a 的引用 del a del b 在这个例子中,del 语句将会减少a 和 b 的引用计数,并销毁用于引用底层对象的名称。然而,因为每个对象都包含一个对其他对象的引用,所以引用计数不会归零,对象也不会被销毁(从而导致内存泄漏)。为了解决这个问题,解释器会定期执行一个周期检测器,搜索不可访问的对象周期并删除它们。解释器在执行过程中会被分配越来越多的内存,在此过程中,会定期运行周期检测算法。使用 gc 模块中的函数可以准确调整和控制该算法的行为。