CPython 引用计数
了解 CPython 如何通过引用计数跟踪对象的生命周期。
CPython 引用计数 是 CoddyKit 上的免费 Python Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Python Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Python Academy 课程共包含 4 节课。
什么是引用计数
CPython 会跟踪每个对象的引用计数。当计数降为 0 时,对象会立即被释放。这是 Python 的主要内存管理机制。
import sys
x = [1, 2, 3]
print(sys.getrefcount(x)) # 2 (x + getrefcount arg)
y = x
print(sys.getrefcount(x)) # 3sys.getrefcount()
sys.getrefcount(obj) 会返回当前引用计数。请注意:调用它会额外增加 1 个引用(函数参数引用)。
import sys
a = "hello"
print(sys.getrefcount(a)) # baseline (may be high for interned strings)
b = a
print(sys.getrefcount(a)) # one more
del b
print(sys.getrefcount(a)) # back to baseline增加和减少
每次赋值都会增加计数;每次删除或重新赋值都会减少计数。当计数达到 0 时,CPython 会调用对象的释放器。
x = [] # refcount = 1
y = x # refcount = 2
z = [x] # refcount = 3 (list holds a reference)
del y # refcount = 2
z.clear() # refcount = 1
del x # refcount = 0 → deallocated对象驻留
CPython 会驻留小整数(-5 到 256)以及许多字符串字面量,从而复用同一个对象。通过 is 比较可以观察到这一点。
a = 256
b = 256
print(a is b) # True (interned)
c = 257
d = 257
print(c is d) # may be False (not interned)弱引用
弱引用不会增加引用计数。当您希望观察对象但不拥有对象,又不想阻止其进行垃圾回收时,弱引用非常有用。
import weakref
class BigObject:
pass
obj = BigObject()
ref = weakref.ref(obj)
print(ref()) # <BigObject instance>
del obj
print(ref()) # None (object was collected)引用循环
如果 A 引用 B,而 B 引用 A,那么即使两者都不可达,它们也都不会达到 0。CPython 的循环垃圾回收器可以检测并回收这些对象。
a = []
b = [a]
a.append(b) # cycle: a → b → a
del a, b
# Both are unreachable but refcount > 0
# The cyclic GC collects themgc 模块
gc 模块提供循环垃圾回收器。它会自动运行,也可以手动触发或禁用。
import gc
gc.collect() # force a collection cycle
print(gc.get_count()) # (gen0, gen1, gen2) allocation counts
print(gc.get_threshold()) # when each generation is collected内存池(pymalloc)
CPython 会从自己的内存池(pymalloc)中分配小对象(小于 512 字节),这比为每个小对象调用 malloc 更快。
# You do not call pymalloc directly — CPython uses it internally
# for all Python-level object allocations
# The pool pre-allocates large memory arenas
# and carves them into pools of fixed-size blocks__del__ 终结器
当对象的引用计数达到 0 时(如果对象处于循环中,则可能更晚),会调用 __del__。请避免依赖它来执行关键清理操作,改用上下文管理器。
class Resource:
def __del__(self):
print(f"Freeing {self}")
# Better: use context manager
class SafeResource:
def __enter__(self): return self
def __exit__(self, *a): self.cleanup()使用 __slots__ 创建紧凑对象
使用 __slots__ 的对象占用更少内存,也需要更少的分配,从而减轻垃圾回收压力。
import sys
class Reg:
def __init__(self, x, y): self.x, self.y = x, y
class Slotted:
__slots__ = ("x","y")
def __init__(self, x, y): self.x, self.y = x, y
print(sys.getsizeof(Reg(1,2))) # ~48 + dict overhead
print(sys.getsizeof(Slotted(1,2))) # ~48 (no dict)禁用循环垃圾回收器
对于不会创建引用循环的程序,禁用循环垃圾回收器可以提高吞吐量。
import gc
gc.disable() # no cyclic GC — objects are only freed by refcounting
# Use only if you KNOW no cycles are created
# (pure functional/immutable data)
gc.enable() # re-enable快速检查
CPython 何时会立即释放一个对象?
总结
CPython 通过引用计数跟踪对象。计数达到 0 → 立即释放。引用循环由循环垃圾回收器(gc 模块)处理。请避免使用 __del__ 执行关键清理操作,改用上下文管理器。请使用 __slots__ 减少每个对象占用的内存。
常见问题解答
「CPython 引用计数」课时是免费的吗?
是的 — 「CPython 引用计数」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Python Academy 课程的其余内容,请升级到 CoddyKit PRO。 Python Academy 课程共包含 4 节课。
「CPython 引用计数」这节课中我会学到什么?
了解 CPython 如何通过引用计数跟踪对象的生命周期。 你通过在浏览器中直接运行的动手代码来练习 Python Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Python Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Python Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「CPython 引用计数」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Python Academy 课中编写并运行代码吗?
能。每节 Python Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。