垃圾回收器与循环引用
了解 gc 模块如何处理引用循环。
垃圾回收器与循环引用 是 CoddyKit 上的免费 Python Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Python Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Python Academy 课程共包含 4 节课。
为什么引用计数还不够
引用计数无法回收参与引用循环的对象——每个对象都持有对另一个对象的引用,因此任何一个对象的计数都不会降为 0。
# Reference cycle: a → b → a
a = {}
b = {"other": a}
a["other"] = b
del a, b
# Both objects are unreachable but refcount > 0
# Only the cyclic GC can collect themgc 模块
gc 是循环垃圾回收器。它会定期扫描被跟踪的对象,查找不可达的循环并将其回收。
import gc
gc.collect() # trigger immediately
print(gc.garbage) # objects with __del__ in cycles (cannot auto-collect)分代回收
垃圾回收器使用三代。新对象从第 0 代开始。经历一次回收后仍然存活的对象会晋升。第 0 代的回收频率最高。
import gc
print(gc.get_count()) # (gen0, gen1, gen2) allocations since last collection
print(gc.get_threshold()) # (700, 10, 10) — thresholds for each generation
gc.set_threshold(1000, 15, 10) # tune使用 gc.get_referents 检测循环
检查一个对象引用了哪些对象,这有助于理解某个对象为何没有被回收。
import gc
a = []
b = [a]
a.append(b)
for ref in gc.get_referents(a):
print(ref) # shows bgc.get_objects()
gc.get_objects() 会返回当前由垃圾回收器跟踪的所有对象。这对于查找内存泄漏很有用。
import gc
before = len(gc.get_objects())
create_lots_of_objects()
after = len(gc.get_objects())
print(f"Leaked: {after - before} objects")避免循环
请设计数据结构以避免循环:对反向引用使用弱引用,使用 ID 代替直接引用,或者在对象离开作用域前显式打破循环。
import weakref
class Node:
def __init__(self, parent):
# weakref avoids cycle:
self.parent = weakref.ref(parent)
class Tree:
def __init__(self):
self.child = Node(self) # no hard cycle__del__ 与循环
包含 __del__ 且参与循环的对象无法被自动回收,而是会被放入 gc.garbage。
import gc
class Leaky:
def __del__(self): pass
a = Leaky()
b = Leaky()
a.other = b
b.other = a
del a, b
gc.collect()
print(gc.garbage) # [Leaky, Leaky] — cannot collectgc.freeze() — Python 3.7+
gc.freeze() 会冻结当前被跟踪的对象,使它们永远不会被回收。在启动后运行时间较长的进程中使用它,可以减少垃圾回收暂停。
import gc
# After all globals and imports are in place:
gc.freeze() # no GC scanning for these objects ever again
# Useful for: gunicorn pre-fork, background servers禁用和重新启用垃圾回收器
在确定不存在循环的紧密循环中,请禁用循环垃圾回收器;之后重新启用它,并强制执行一次回收。
import gc
gc.disable()
try:
result = tight_loop_no_cycles()
finally:
gc.enable()
gc.collect()isenabled 和 isfinalized
请使用 gc.isenabled() 检查垃圾回收器状态,并使用 gc.is_finalized(obj) 检测对象是否正在终结。
import gc
print(gc.isenabled()) # True by default
obj = object()
print(gc.is_finalized(obj)) # False (still alive)内存泄漏模式
常见的 Python 内存泄漏原因包括:无限增长的全局缓存、未解除注册的事件监听器、包含 __del__ 的引用循环,以及泄漏引用的 C 扩展。
# Typical leaks:
global_cache = {} # grows forever — use lru_cache with maxsize
class EventBus:
listeners = []
# Listeners hold references to objects:
# @classmethod
# def on(cls, fn): cls.listeners.append(fn)
# Must call off() to detach快速检查
参与引用循环且包含 __del__ 的对象会发生什么?
总结
循环垃圾回收器会处理引用计数无法处理的引用循环。它使用三代,并将存活对象晋升。请使用弱引用避免循环。循环中包含 __del__ 的对象会进入 gc.garbage。请在启动后使用 gc.freeze(),以减少长时间运行的服务器中的垃圾回收暂停。
常见问题解答
「垃圾回收器与循环引用」课时是免费的吗?
是的 — 「垃圾回收器与循环引用」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Python Academy 课程的其余内容,请升级到 CoddyKit PRO。 Python Academy 课程共包含 4 节课。
「垃圾回收器与循环引用」这节课中我会学到什么?
了解 gc 模块如何处理引用循环。 你通过在浏览器中直接运行的动手代码来练习 Python Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Python Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Python Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「垃圾回收器与循环引用」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Python Academy 课中编写并运行代码吗?
能。每节 Python Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。