Skip to content

Python垃圾回收机制

Python程序在运行的时候, 需要在内存中开辟一块空间, 用于存放运行时产生的临时变量, 计算完成后, 再将结果输出到永久性存储器中。如果数据量过大, 内存空间管理不善就很容易出现OOM(out of memory)的现象, 程序就会被系统中断。因此, 需要一种内存管理机制, Python垃圾回收机制就是一种自动内存管理机制, 在某些情况下, 开发者还需要手动进行垃圾回收。

内存泄漏

这里的泄漏, 并不是说内存出现了信息安全的问题, 被恶意程序利用了, 而是指程序没有设计好, 导致程序未能释放已经不再使用的内存。对于不再使用的内存空间, Python通过如下机制来回收。

引用计数(Reference Counting)

Python中一切皆为对象, 每个对象都有一个引用计数器, 记录有多少引用指向该对象, 当这个对象的引用计数为0的时候, 这个对象就成为了垃圾, Python会立即回收该对象的内存。

  • 引用计数增加的情况:
    • 对象被创建时(如 x = 10
    • 对象被赋值给其他变量时(如 y = x
    • 对象被作为参数传递给函数时引用计数会加2, 一次来自参数引用, 一次来自函数调用栈引用
    • 对象被添加到容器中时(如 list.append(x)
  • 引用计数减少的情况:
    • 变量被显式删除时(如 del x
    • 变量被赋值为None时
    • 对象离开作用域时(如函数执行完毕)
    • 对象从容器中被移除时(如 list.remove(obj)需要注意
  • 在 Python 中, 当一个对象被创建并赋值给一个变量时, 它的引用计数会被初始化为 1
  • 引用计数回收机制无法处理循环引用(即两个或多个对象互相引用, 但外部没有引用指向他们的情况) **

循环垃圾收集器(Cycle Collector)

为解决循环引用问题, Python引入了循环垃圾收集器, 它是gc模块的一部分

  • 工作原理
    • 循环垃圾收集器会定期检查不可达对象(即无法通过引用链访问到的对象)
    • 它通过标记-清除(Mark-and-Sweep)算法来检测和回收循环引用的对象
  • 触发条件
    • 当对象的引用计数不为0, 但无法通过任何引用链访问到时
    • gc模块的阈值被触发时(如对象分配数量达到一定值)
  • 手动控制
    • 可以通过gc.collect()手动触发垃圾回收
    • 可以通过gc.disable()gc.enable()禁用或启用垃圾回收 循环垃圾收集器虽然解决了循环引用问题, 但是需要额外的计算资源, 可能会影响性能。

分代回收(Generational Collection)

Python 的垃圾回收器还采用了分代回收策略, 基于对象的存活时间将对象分为不同的代(Generation)

  • 分代
    • 新创建的对象属于第0
    • 如果对象在依次垃圾回收后仍然存活, 则会被移动到下一代
    • Python默认分3代(0, 1, 2)
  • 回收频率
    • 0代的对象回收频率最高
    • 2代的对象回收频率最低

每一代启动自动垃圾回收的阈值,则是可以单独指定的。当垃圾回收容器中新增对象减去删除对象达到相应的阈值时,就会对这一代对象启动垃圾回收。新生的对象更有可能被垃圾回收,而存活更久的对象也有更高的概率继续存活。这样做提高了垃圾回收的效率, 因为大部分对象的生命周期较短。

弱引用(Weak References)

Python 提供了 weakref 模块, 用于创建弱引用。弱引用不会增加对象的引用计数, 因此不会阻止对象被回收。

  • 使用场景
    • 缓存
    • 避免循环引用
python
import weakref

class Myclass:
  pass

obj = Myclass() # obj引用Myclass实例, Myclass实例的引用计数是1
weak_obj = weakref.ref(obj) # 创建对obj的弱引用, 此时Myclass实例的引用计数仍然是1
print(weak_obj()) # 输出: <__main__.Myclass object at 0x7fb28a4ee640>

del obj   # 显式删除对Myclass实例的引用, Myclass的引用实例为0, 被Python回收
print(weak_obj()) # 输出: None

手动管理内存

虽然 Python 有自动垃圾回收机制, 但在某些情况下, 开发者可能需要手动管理内存:

  • 使用 del 删除不再需要的对象
  • 使用 gc.collect() 手动触发垃圾回收
  • 使用 with 语句管理资源(如文件、网络连接等)



(暂时结束, 持续更新)

Last Updated:

Released under the MIT License.