人人都会AI编程

引用计数增减规则

更新时间:2026-07-12

Python 的每个对象内部都有一个计数器,记录当前有多少个引用指向这个对象。这个计数器就叫引用计数。当引用计数变为零时,对象就会被立即回收,内存被释放。理解增减规则能帮你写出更稳、更省内存的代码。


引用计数增加的情况

以下操作会让对象的引用计数 +1

  1. 创建对象

即便没人赋值,对象刚诞生时引用计数就是 1。

   a = []          # 列表对象引用计数 = 1
   
  1. 新的引用指向该对象
   b = a           # a 和 b 指向同一个列表,引用计数变为 2
   
  1. 将对象放入容器
   lst = [a]       # 列表 lst 内部持有对 a 所指数列表的引用,引用计数 +1 → 3
   dic = {'key': a}
   
  1. 作为函数参数传递

调用函数时,实参会临时让形参指向同一个对象,引用计数短暂增加。

   def foo(x):     # x = a,引用计数 +1
       ...
   foo(a)          # 进入函数时 a 引用计数 +1,函数返回后恢复
   
  1. 作为另一个对象的属性
   obj.data = a    # obj.data 也指向同一对象,引用计数 +1
   

引用计数减少的情况

以下操作会让引用计数 -1

  1. 引用被销毁(离开作用域)

比如函数返回后,函数内部的局部变量会被销毁,引用计数对应减少。

   def bar():
       x = [1, 2]  # 进入函数时引用计数 1
   bar()           # 返回后 x 销毁,列表对象引用计数归零,立即回收
   
  1. 引用被重新指向其他对象
   a = [1, 2]      # 列表 [1,2] 引用计数 1
   a = "hello"     # a 现在指向字符串,原来 [1,2] 引用计数 -1
   
  1. 容器对象被移除或销毁
   lst = [a]       # a 指向的对象引用计数 +1
   lst = None      # lst 被置空,内部元素 a 的引用释放,引用计数 -1
   
  1. 显式调用 del
   del a           # 删除变量 a,它原来指向的对象引用计数 -1
   

实际中要注意的细节

  • 不可变类型与对象复用

小整数、短字符串等不可变对象可能会被 Python 内部缓存,即使引用计数降到零也不一定会立刻释放。例如:

  a = 10
  b = 10          # 可能指向同一个整数对象 10,引用计数会大于 1
  

这是 Python 的一种优化,日常编码不必刻意关注。

  • 循环引用会让引用计数失效

如果两个对象互相引用,即使外界已经没有变量指向它们,它们的引用计数也永远不会归零,从而导致内存泄漏。这就是为什么垃圾回收还需要标记-清除分代回收来辅助。典型案例:

  a = []
  b = []
  a.append(b)     # b 中的列表引用计数 +1
  b.append(a)     # a 中的列表引用计数 +1
  del a
  del b           # 两个对象互相持有引用,引用计数仍为 1,不会被引用计数机制回收
  
  • 很多内置操作会临时增加引用计数

例如使用 sys.getrefcount() 查看计数时,函数调用本身会造成引用计数的临时增加,所以看到的值通常比你预期的多 1。

理解增减规则,能帮你更清楚地判断对象何时会被销毁,也有助于在使用自定义容器、文件、网络连接等资源时,避免出现“明明不用了但内存迟迟不释放”的困惑。