Python 使用两种机制自动管理内存:引用计数(主要方式)外加一个处理引用循环的循环垃圾回收器。你永远不需要手动释放内存,但理解其工作原理有助于解释内存行为和内存泄漏。
引用计数——主要机制
python
sys
x = [, , ]
y = x
sys.getrefcount(x)
y
x
Python 使用两种机制自动管理内存:引用计数(主要方式)外加一个处理引用循环的循环垃圾回收器。你永远不需要手动释放内存,但理解其工作原理有助于解释内存行为和内存泄漏。
sys
x = [, , ]
y = x
sys.getrefcount(x)
y
x
每个对象都会跟踪有多少个引用指向它。当计数降到零时,内存会立即被释放。这种方式是确定性且及时的——一旦没有任何引用指向对象,对象就会被销毁。
# reference counting alone can't free this:
a = {}
b = {}
a["b"] = b # a references b
b["a"] = a # b references a → they reference EACH OTHER
del a, b # the names are gone, but the objects still reference each other
# → refcounts never reach 0 → they'd leak with refcounting alone
两个相互引用的对象会让彼此的引用计数始终大于零,即使它们已经无法再被访问——这就形成了纯引用计数无法回收的循环。
import gc
# the generational GC periodically finds and frees unreachable CYCLES
gc.collect() # manually trigger a collection
gc.get_count() # objects tracked per generation
Python 的辅助性分代垃圾回收器会周期性地检测并释放这些无法访问的循环。它是分代的——较新的对象会被更频繁地检查(大多数对象都是“朝生暮死”的),这样更高效。
✓ Most memory is reclaimed promptly via reference counting
⚠️ Leaks still happen from REACHABLE references that outlive their usefulness:
- global caches/lists that only grow
- objects held by long-lived closures or class attributes
- registered callbacks/listeners never removed
GC 能处理循环,但无法释放仍然可访问的对象——现实中常见的“泄漏”正是无意中让引用保持存活(例如一个不断增长的全局缓存)。
- Small integers (-5..256) and short strings are CACHED/interned (shared, reused)
- CPython uses memory pools (pymalloc) for small objects (efficiency)
理解 Python 的内存模型——及时的引用计数加上循环 GC——可以解释许多真实行为:为什么对象通常会被立即释放,为什么引用循环需要 GC,以及最关键的,为什么 Python 中的“内存泄漏”来自于可访问的引用(不断增长的缓存、残留的闭包、未被移除的回调),而不是被遗忘的释放操作。
这些知识对于诊断长时间运行服务中的内存增长、以及编写注重内存的代码至关重要——要知道,解决办法通常是切断引用(限制缓存大小、清理注册项),而不是指望 GC 能替你做什么。