Python 面试题

Python垃圾回收:引用计数+循环垃圾回收

"Python的垃圾回收机制是什么?"

为什么面试官会问这道题

Python GC是中阶面试题,考你理解Python和Java的本质差异——Python主力是引用计数,循环回收是辅助。答对这题说明语言深度。

如何回答

  1. 1

    主力机制:引用计数。每个对象有refcount字段,归零立即释放。所以大部分对象回收是确定性的,不像JVM GC有停顿。

  2. 2

    引用计数解决不了的问题:循环引用。a引用b,b引用a,refcount永远不归零。

  3. 3

    解法:分代循环GC(gc模块)。周期性扫容器对象找不可达环并回收。三代(0/1/2),存活的对象升代,高代扫得少。

  4. 4

    触发时机:分配-释放的净值超过阈值时触发,默认(700, 10, 10)。不是时间触发,是分配压力触发。

  5. 5

    实战含义:gc.collect()手动触发;gc.disable()在性能敏感段用;weakref专门打破已知循环(比如父子引用)。

参考回答示例

Python的GC是两层机制。主力是引用计数——CPython每个对象的C结构里有refcount字段,每次引用操作都+1或-1。归零立即释放——tp_dealloc调用、析构函数跑、内存还给分配器。这和JVM本质不同——大部分Python对象回收是确定性同步的,不用等GC周期。代价是每次赋值和函数调用都有计数开销,这也是CPython比JIT过的JVM慢的原因之一。第二个缺点:引用计数处理不了循环。对象a持有b的引用,b持有a的引用,refcount互相算着,外面没人引用时两个都不会归零。Python用分代循环GC解决——gc模块周期性扫容器对象(list、dict、class等任何可以装引用的),用mark-sweep找不可达环并释放。三代:新建容器进gen 0,存活升gen 1,再存活升gen 2。高代扫得少,基于"活久了的对象更可能继续活"的假设。触发阈值默认(700, 10, 10)——gen 0在净分配700次后扫、gen 1在gen 0扫10次后扫,以此类推。不是时间触发,纯粹分配压力触发。实战含义:gc.collect()强制全量回收,内存敏感操作前可以调。gc.disable()在性能关键热路径里用——避免不可预测停顿,之后要记得enable+collect。提前知道的"逻辑循环"——比如DOM树或树数据结构的父子引用——用weakref.ref或weakref.proxy根本不创建循环,让引用计数确定性回收,比等循环GC更高效。

实用技巧

  • 先讲引用计数再讲循环回收——顺序反映真实架构。

  • "引用计数是每次操作的开销"是CPython底层信号。

  • 能讲weakref作为"已知循环的逃生口"是实战经验信号。

  • 忘了默认阈值(700, 10, 10),即答侠可以实时提示。

常见问题

为什么Python的GC不像JVM?

Python先引用计数后循环回收。JVM不用引用计数是因为多线程下原子++/--开销大——Python有GIL让refcount更新免原子操作。

GIL和GC有什么关系?

GIL让refcount更新串行化不用原子操作。去GIL(3.13+的nogil工作)要求refcount原子化或换方案。

什么时候用gc.disable()?

很少。profiling显示GC停顿影响性能的热循环里临时关,用完一定要enable + collect。

面试时担心忘词?即答侠实时助你

即答侠 AI 实时监听面试对话,自动识别问题并即时生成回答建议——无感辅助,让你从容应对每一道题。

免费试用即答侠