Python 垃圾回收机制:引用计数、循环引用和分代 GC
很多人第一次关心 Python 的垃圾回收,不是因为写了多复杂的代码,而是因为遇到了几个很实际的问题: del obj 之后,内存为什么没有立刻降下来? 明明没有全局变量引用某个对象,它为什么还活着? Python 不是有垃圾回收吗,为什么还会出现内存泄漏? gc.collect() 到底该不该在业务代码里手动调用? 这些问题背后其实是同一个机制:Python 的内存回收不是单一算法,而是几层机制一起工作。平时我们说的“Python 垃圾回收”,在最常见的 CPython 解释器里,主要由两部分组成: 引用计数:对象引用数变成 0 时,通常立刻释放。 循环垃圾回收器:专门处理引用计数解决不了的循环引用。 这篇文章主要讨论 CPython,因为日常用 python 命令启动的解释器,大多数情况下就是 CPython。PyPy、Jython、IronPython 等实现可以采用不同策略,所以不要把本文所有细节直接套到每一种 Python 实现上。 先从变量和对象的关系说起 在 Python 里,变量不是装对象的盒子,更像是贴在对象上的名字。 a = [1, 2, 3] b = a 这段代码里没有创建两个列表。它创建了一个列表对象,然后让 a 和 b 都指向它: a ─┐ ├──> [1, 2, 3] b ─┘ 执行: del a 删除的也不是列表对象本身,而是删除名字 a 到列表对象的那条引用。因为 b 还指向这个列表,所以对象仍然活着: b ───> [1, 2, 3] 这就是理解 Python 垃圾回收的入口:对象什么时候能被回收,取决于还有没有地方能继续访问它。 del 的含义也要顺手纠正一下:del name 删除的是名字绑定;del obj.attr 删除的是属性引用;del some_list[i] 删除的是容器里的一个引用。它们都不等于“立刻把某块内存还给操作系统”。 ...