0Pricing
Python Academy · 课时

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))   # 3

sys.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 them

gc 模块

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. CPython 引用计数
  2. 垃圾回收器与循环引用
  3. 使用 cProfile 和 line_profiler 进行性能分析
  4. 使用 tracemalloc 进行内存分析
← 返回 Python Academy