面试题答案
一键面试标记 - 清除算法处理循环引用流程
标记阶段
- 根对象集合确定:Python将一些全局变量、栈上的变量等作为根对象集合。这些根对象是程序可以直接访问到的对象。例如,模块级别的全局变量,函数调用栈中的局部变量等。
- 遍历与标记:从根对象集合出发,通过对象之间的引用关系进行深度优先搜索(DFS)或广度优先搜索(BFS)。当访问到一个对象时,如果该对象尚未被标记,就为其打上标记。这意味着该对象是可达的,即从程序可访问的根对象出发能够到达该对象。例如,假设存在一个对象A,它引用了对象B,当从根对象遍历到A时,标记A,接着通过A对B的引用,标记B。
清除阶段
- 未标记对象识别:在标记阶段完成后,所有未被标记的对象被认为是不可达的。这些对象由于没有从根对象出发的引用路径,意味着程序无法再访问到它们。
- 内存回收:Python垃圾回收器会回收这些未标记对象所占用的内存空间,将其归还给操作系统或内存池,以便重新分配使用。
Python源码层面实现
- 数据结构:在Python源码中,
gcmodule.c
文件实现了垃圾回收机制。使用gc_generation
结构体来管理不同代的垃圾回收。每个对象在Python内部有一个PyObject
结构体,其中包含引用计数等信息,同时还可能包含与垃圾回收相关的标记位等。 - 标记实现:标记过程通过
_PyObject_GC_Track
和_PyObject_GC_UnTrack
等函数来跟踪对象的可达性。在标记阶段,会遍历根对象集合,调用_PyObject_GC_Track
标记可达对象。例如,在遍历对象的引用关系时,遇到新的对象就调用_PyObject_GC_Track
标记它。 - 清除实现:清除阶段在
gc_collect
函数中完成。该函数会遍历所有对象,检查对象的标记位。对于未标记的对象,调用_PyObject_GC_Del
等函数来释放对象占用的内存,从而完成垃圾回收。