标记清除算法工作原理
- 标记阶段:
- Python 会在堆内存中维护一个双向链表,用于管理所有对象。垃圾回收器从根对象集合(如全局变量、栈上的变量等)出发,通过引用关系遍历整个对象图。
- 对于每一个可达的对象(即从根对象通过引用链能够访问到的对象),垃圾回收器会为其设置一个标记位,标记该对象为存活对象。
- 清除阶段:
- 标记完成后,垃圾回收器再次遍历堆内存中的所有对象。
- 对于那些没有被标记(即标记位未设置)的对象,垃圾回收器认为这些对象是不可达的,也就是垃圾对象,会将这些对象所占用的内存空间回收,并将其从对象链表中移除。
有效避免内存泄漏场景
- 循环引用场景:
- 当两个或多个对象相互引用,形成循环引用时,如果没有标记清除算法,这些对象即使在程序逻辑上不再被需要,也无法被正常回收,从而导致内存泄漏。
- 例如,在Python中,如下代码:
class A:
def __init__(self):
self.b = None
class B:
def __init__(self):
self.a = None
a = A()
b = B()
a.b = b
b.a = a
a = None
b = None
- 这里
a
和b
对象相互引用,在将a
和b
赋值为None
后,这两个对象已经没有外部引用,但由于循环引用,如果没有标记清除算法,它们不会被回收。标记清除算法通过从根对象出发标记可达对象,这两个相互引用但从根不可达的对象不会被标记,最终会在清除阶段被回收,有效避免了这种因循环引用导致的内存泄漏。
- 复杂数据结构内部引用场景:
- 在一些复杂的数据结构,如嵌套的列表、字典或自定义的复杂类结构中,内部对象之间可能存在复杂的引用关系。
- 例如,一个多层嵌套的字典结构,当外部对这个字典的引用被删除后,字典内部对象之间的引用可能使得这些对象无法被正常回收。标记清除算法能够从根对象遍历到这些复杂结构内部的对象,正确标记存活对象,回收不可达对象,避免内存泄漏。