gpt4 book ai didi

Python垃圾回收

转载 作者:我是一只小鸟 更新时间:2023-09-07 22:51:54 32 4
gpt4 key购买 nike

Python版本

v3.9.17 。

分析代码的过程比较枯燥,可以直接跳转到总结.

只能被其他对象引用类型

比如:longobject、floatobject 。

floatobject

以floatobject为例子来分析,先看看结构定义 。

                        
                          typedef struct {
    PyObject_HEAD
    double ob_fval;
} PyFloatObject;

// 展开PyObject_HEAD后
typedef struct {
    PyObject ob_base;
    double ob_fval;
} PyFloatObject;

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
} PyObject;

                        
                      

在PyObject中的 _PyObject_HEAD_EXTRA ,只有在编译时指定 --with-trace-refs 才有效,这里忽略即可.

                        
                          ./configure --with-trace-refs

                        
                      

可以看到在PyObject里有一个 ob_refcnt 的属性,这个就是引用计数。 当对引用计数减为0时,就会调用各类型对应的析构函数.

                        
                          define Py_DECREF(op) _Py_DECREF(_PyObject_CAST(op))

void _Py_Dealloc(PyObject *op)
{
    destructor dealloc = Py_TYPE(op)->tp_dealloc;
    (*dealloc)(op);
}

static inline void _Py_DECREF(PyObject *op)
{
    if (--op->ob_refcnt != 0) {
    }
    else {
        _Py_Dealloc(op);
    }
}

                        
                      

能引用其他对象的类型

比如listobject,dictobject... 。

listobject

以listobject为例子来分析,先看看结构定义 。

                        
                          typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

// 展开 PyObject_VAR_HEAD
typedef struct {
    PyVarObject ob_base;
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;

                        
                      

可以看出, PyObject_VAR_HEAD 也就比 PyObject_HEAD 多了一个 Py_ssize_t ob_size 而已,这个属性是用来表示这个可变对象里元素数量.

因为可以引用其他对象,就有可能会出现环引用问题,这种问题如果再使用引用计数来作为GC就会出现问题.

                        
                          lst1 = []
lst2 = []
lst1.append(lst2)
lst2.append(lst1)

                        
                      

当然这种情况可以使用弱引用,或者手动解除环引用。这些解决方案这里不深入,现在主要看看python是怎样应对这种情况.

对于这类型的对象在申请内存的时候调用的是 PyObject_GC_New ,而不可变类型是用 PyObject_MALLOC 。为了减少篇幅,删掉了一些判断逻辑.

                        
                          typedef struct {
    // Pointer to next object in the list.
    // 0 means the object is not tracked
    uintptr_t _gc_next;

    // Pointer to previous object in the list.
    // Lowest two bits are used for flags documented later.
    uintptr_t _gc_prev;
} PyGC_Head;

#define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))

static PyObject * _PyObject_GC_Alloc(int use_calloc, size_t basicsize)
{
    PyThreadState *tstate = _PyThreadState_GET();
    GCState *gcstate = &tstate->interp->gc;

    size_t size = sizeof(PyGC_Head) + basicsize;

    PyGC_Head *g;
    g = (PyGC_Head *)PyObject_Malloc(size);

    g->_gc_next = 0;
    g->_gc_prev = 0;
    gcstate->generations[0].count++; /* number of allocated GC objects */
    if (/* 判断是否可以执行GC */)
    {
        gcstate->collecting = 1;
        collect_generations(tstate);
        gcstate->collecting = 0;
    }
    PyObject *op = FROM_GC(g);
    return op;
}

                        
                      

在可变对象中,python又加上了一个 PyGC_Head 。通过这个 PyGC_Head 将listobject链接到gc列表中.

在分配完listobject内存后,紧接着调用 _PyObject_GC_TRACK ,链接到gc列表中.

                        
                          static inline void _PyObject_GC_TRACK_impl(const char *filename, int lineno,
                                           PyObject *op)
{
    PyGC_Head *gc = _Py_AS_GC(op);

    PyThreadState *tstate = _PyThreadState_GET();
    PyGC_Head *generation0 = tstate->interp->gc.generation0;
    PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev);
    _PyGCHead_SET_NEXT(last, gc);
    _PyGCHead_SET_PREV(gc, last);
    _PyGCHead_SET_NEXT(gc, generation0);
    generation0->_gc_prev = (uintptr_t)gc;
}

                        
                      

通过这里的变量名,可以猜测使用到了分代垃圾回收.

分代回收

python手动执行垃圾回收一般调用gc.collect( generation=2 )函数.

                        
                          #define NUM_GENERATIONS 3

#define GC_COLLECT_METHODDEF    \
    {"collect", (PyCFunction)(void(*)(void))gc_collect, METH_FASTCALL|METH_KEYWORDS, gc_collect__doc__},

static PyObject *
gc_collect(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
    PyObject *return_value = NULL;
    int generation = NUM_GENERATIONS - 1;
    Py_ssize_t _return_value;

    _return_value = gc_collect_impl(module, generation);
    if ((_return_value == -1) && PyErr_Occurred()) {
        goto exit;
    }
    return_value = PyLong_FromSsize_t(_return_value);

exit:
    return return_value;
}

                        
                      

具体执行在 gc_collect_impl 函数中,接着往下 。

                        
                          static Py_ssize_t gc_collect_impl(PyObject *module, int generation)
{
    PyThreadState *tstate = _PyThreadState_GET();

    GCState *gcstate = &tstate->interp->gc;
    Py_ssize_t n;
    if (gcstate->collecting) {
        /* already collecting, don't do anything */
        n = 0;
    }
    else {
        gcstate->collecting = 1;
        n = collect_with_callback(tstate, generation);
        gcstate->collecting = 0;
    }
    return n;
}

                        
                      

可以看到,如果已经在执行GC,则直接返回。接着看 collect_with_callback 。

                        
                          static Py_ssize_t
collect_with_callback(PyThreadState *tstate, int generation)
{
    assert(!_PyErr_Occurred(tstate));
    Py_ssize_t result, collected, uncollectable;
    invoke_gc_callback(tstate, "start", generation, 0, 0);
    result = collect(tstate, generation, &collected, &uncollectable, 0);
    invoke_gc_callback(tstate, "stop", generation, collected, uncollectable);
    assert(!_PyErr_Occurred(tstate));
    return result;
}

                        
                      

其中 invoke_gc_callback 是调用通过 gc.callbacks 注册的回调函数,这里我们忽略,重点分析 collect 函数.

collect函数签名 这段代码很长,我们拆分开来分析,这里会去除掉一些DEBUG相关的逻辑.

                        
                          static Py_ssize_t collect(PyThreadState *tstate, int generation,Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable, int nofail);

                        
                      
  1. 将新生代的对象合并到指定代的对象列表中。
                        
                          /* merge younger generations with one we are currently collecting */
for (i = 0; i < generation; i++) {
    gc_list_merge(GEN_HEAD(gcstate, i), GEN_HEAD(gcstate, generation));
}

                        
                      

比如调用 gc.collect(2) ,就表示启动全部的垃圾回收。这里就会将第0、1代的对象合并到第2代上。合并之后第0、1代上就空了,全部可GC的对象都在第2代上.

  1. 推断不可达对象
                        
                          /* handy references */
young = GEN_HEAD(gcstate, generation);
if (generation < NUM_GENERATIONS-1)
    old = GEN_HEAD(gcstate, generation+1);
else
    old = young;
validate_list(old, collecting_clear_unreachable_clear);

deduce_unreachable(young, &unreachable);

                        
                      

这里的young指针指向第2代的链表头, validate_list 做校验,这里忽略,重点在 deduce_unreachable 函数中.

                        
                          static inline void
deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
    validate_list(base, collecting_clear_unreachable_clear);
    update_refs(base);  // gc_prev is used for gc_refs
    subtract_refs(base);
    gc_list_init(unreachable);
    move_unreachable(base, unreachable);  // gc_prev is pointer again
    validate_list(base, collecting_clear_unreachable_clear);
    validate_list(unreachable, collecting_set_unreachable_set);
}

                        
                      

首先调用 update_refs 更新引用计数 。

                        
                          static inline void
gc_reset_refs(PyGC_Head *g, Py_ssize_t refs)
{
    g->_gc_prev = (g->_gc_prev & _PyGC_PREV_MASK_FINALIZED)
        | PREV_MASK_COLLECTING
        | ((uintptr_t)(refs) << _PyGC_PREV_SHIFT);
}

static void
update_refs(PyGC_Head *containers)
{
    PyGC_Head *gc = GC_NEXT(containers);
    for (; gc != containers; gc = GC_NEXT(gc)) {
        gc_reset_refs(gc, Py_REFCNT(FROM_GC(gc)));
        _PyObject_ASSERT(FROM_GC(gc), gc_get_refs(gc) != 0);
    }
}

                        
                      

这里的逻辑就是遍历所有对象,然后赋值_gc_prev,设置为收集中的标识 PREV_MASK_COLLECTING ,然后将引用计数赋值给_gc_prev 。最后_gc_prev的内容如下.

更新完_gc_prev后,就开始调用 subtrace_refs ,遍历对象中的元素,判断元素是否也是可GC对象并且有收集中标记,如果是则减去该对象的计数。注意这里减去的是_gc_prev中的计数,而不是真正的计数 ob_refcnt .

                        
                          static int
visit_decref(PyObject *op, void *parent)
{
    _PyObject_ASSERT(_PyObject_CAST(parent), !_PyObject_IsFreed(op));

    if (_PyObject_IS_GC(op)) {
        PyGC_Head *gc = AS_GC(op);
        /* We're only interested in gc_refs for objects in the
         * generation being collected, which can be recognized
         * because only they have positive gc_refs.
         */
        if (gc_is_collecting(gc)) {
            gc_decref(gc);
        }
    }
    return 0;
}

static void
subtract_refs(PyGC_Head *containers)
{
    traverseproc traverse;
    PyGC_Head *gc = GC_NEXT(containers);
    for (; gc != containers; gc = GC_NEXT(gc)) {
        PyObject *op = FROM_GC(gc);
        traverse = Py_TYPE(op)->tp_traverse;
        (void) traverse(FROM_GC(gc),
                       (visitproc)visit_decref,
                       op);
    }
}

                        
                      

更新计数值之后,就开始收集不可达对象,将对象移入到不可达列表中。 unreachable .

                        
                          /* A traversal callback for move_unreachable. */
static int
visit_reachable(PyObject *op, PyGC_Head *reachable)
{
    if (!_PyObject_IS_GC(op)) {
        return 0;
    }

    PyGC_Head *gc = AS_GC(op);
    const Py_ssize_t gc_refs = gc_get_refs(gc);

    if (! gc_is_collecting(gc)) {
        return 0;
    }
    assert(gc->_gc_next != 0);

    if (gc->_gc_next & NEXT_MASK_UNREACHABLE) {
        PyGC_Head *prev = GC_PREV(gc);
        PyGC_Head *next = (PyGC_Head*)(gc->_gc_next & ~NEXT_MASK_UNREACHABLE);
        _PyObject_ASSERT(FROM_GC(prev),
                         prev->_gc_next & NEXT_MASK_UNREACHABLE);
        _PyObject_ASSERT(FROM_GC(next),
                         next->_gc_next & NEXT_MASK_UNREACHABLE);
        prev->_gc_next = gc->_gc_next;  // copy NEXT_MASK_UNREACHABLE
        _PyGCHead_SET_PREV(next, prev);

        gc_list_append(gc, reachable);
        gc_set_refs(gc, 1);
    }
    else if (gc_refs == 0) {
        gc_set_refs(gc, 1);
    }
    else {
        _PyObject_ASSERT_WITH_MSG(op, gc_refs > 0, "refcount is too small");
    }
    return 0;
}

static void
move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
{
    PyGC_Head *prev = young;
    PyGC_Head *gc = GC_NEXT(young);

    while (gc != young) {
        if (gc_get_refs(gc)) {
            PyObject *op = FROM_GC(gc);
            traverseproc traverse = Py_TYPE(op)->tp_traverse;
            _PyObject_ASSERT_WITH_MSG(op, gc_get_refs(gc) > 0,
                                      "refcount is too small");
            (void) traverse(op,
                    (visitproc)visit_reachable,
                    (void *)young);
            _PyGCHead_SET_PREV(gc, prev);
            gc_clear_collecting(gc);
            prev = gc;
        }
        else {
            prev->_gc_next = gc->_gc_next;
            PyGC_Head *last = GC_PREV(unreachable);
            last->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)gc);
            _PyGCHead_SET_PREV(gc, last);
            gc->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)unreachable);
            unreachable->_gc_prev = (uintptr_t)gc;
        }
        gc = (PyGC_Head*)prev->_gc_next;
    }
    // young->_gc_prev must be last element remained in the list.
    young->_gc_prev = (uintptr_t)prev;
    // don't let the pollution of the list head's next pointer leak
    unreachable->_gc_next &= ~NEXT_MASK_UNREACHABLE;
}

                        
                      

这段代码的逻辑是,遍历收集代中的所有对象,判断对象的计数值是否为0 如果等于0,则从收集代中移除,加入不可达列表中,然后打上不可达标记。 如果不等于0,则遍历对象的所有元素,如果元素已经被打上不可达标记,则把该元素从不可达列表中移除,重新加入收集代列表中,并且将计数值设置为1。这是因为父对象可以被访问,那么子对象一定可以被访问.

  1. 把定义了__ del __的对象从不可达对象中移除
                        
                          static int
has_legacy_finalizer(PyObject *op)
{
    return Py_TYPE(op)->tp_del != NULL;
}

static void
move_legacy_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
{
    PyGC_Head *gc, *next;
    assert((unreachable->_gc_next & NEXT_MASK_UNREACHABLE) == 0);

    for (gc = GC_NEXT(unreachable); gc != unreachable; gc = next) {
        PyObject *op = FROM_GC(gc);

        _PyObject_ASSERT(op, gc->_gc_next & NEXT_MASK_UNREACHABLE);
        gc->_gc_next &= ~NEXT_MASK_UNREACHABLE;
        next = (PyGC_Head*)gc->_gc_next;

        if (has_legacy_finalizer(op)) {
            gc_clear_collecting(gc);
            gc_list_move(gc, finalizers);
        }
    }
}

                        
                      

这里的逻辑就比较简单,判断是否定义了__del__函数,如果有,则从不可达列表中删除,加入 finalizers 列表,并且清除收集中标记.

                        
                          /* A traversal callback for move_legacy_finalizer_reachable. */
static int
visit_move(PyObject *op, PyGC_Head *tolist)
{
    if (_PyObject_IS_GC(op)) {
        PyGC_Head *gc = AS_GC(op);
        if (gc_is_collecting(gc)) {
            gc_list_move(gc, tolist);
            gc_clear_collecting(gc);
        }
    }
    return 0;
}

/* Move objects that are reachable from finalizers, from the unreachable set
 * into finalizers set.
 */
static void
move_legacy_finalizer_reachable(PyGC_Head *finalizers)
{
    traverseproc traverse;
    PyGC_Head *gc = GC_NEXT(finalizers);
    for (; gc != finalizers; gc = GC_NEXT(gc)) {
        /* Note that the finalizers list may grow during this. */
        traverse = Py_TYPE(FROM_GC(gc))->tp_traverse;
        (void) traverse(FROM_GC(gc),
                        (visitproc)visit_move,
                        (void *)finalizers);
    }
}

                        
                      

然后再遍历 finalizers 列表中的所有对象,判断对象的每个元素是否也是可GC对象,并且也有收集中标记,如果满足条件,则从不可达列表中删除,加入 finalizers 列表,并且清除收集中标记.

  1. 遍历不可达对象列表,处理弱引用。
  2. 遍历不可达对象列表,为每个对象调用 tp_finalize 函数,如果没有则跳过。
                        
                          static void
finalize_garbage(PyThreadState *tstate, PyGC_Head *collectable)
{
    destructor finalize;
    PyGC_Head seen;

    gc_list_init(&seen);

    while (!gc_list_is_empty(collectable)) {
        PyGC_Head *gc = GC_NEXT(collectable);
        PyObject *op = FROM_GC(gc);
        gc_list_move(gc, &seen);
        if (!_PyGCHead_FINALIZED(gc) &&
                (finalize = Py_TYPE(op)->tp_finalize) != NULL) {
            _PyGCHead_SET_FINALIZED(gc);
            Py_INCREF(op);
            finalize(op);
            assert(!_PyErr_Occurred(tstate));
            Py_DECREF(op);
        }
    }
    gc_list_merge(&seen, collectable);
}

                        
                      
  1. 处理复活的对象
                        
                          static inline void
handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
                           PyGC_Head *old_generation)
{
    // Remove the PREV_MASK_COLLECTING from unreachable
    // to prepare it for a new call to 'deduce_unreachable'
    gc_list_clear_collecting(unreachable);

    // After the call to deduce_unreachable, the 'still_unreachable' set will
    // have the PREV_MARK_COLLECTING set, but the objects are going to be
    // removed so we can skip the expense of clearing the flag.
    PyGC_Head* resurrected = unreachable;
    deduce_unreachable(resurrected, still_unreachable);
    clear_unreachable_mask(still_unreachable);

    // Move the resurrected objects to the old generation for future collection.
    gc_list_merge(resurrected, old_generation);
}

                        
                      

这里主要是上一步会调用 tp_finalize 函数,有可能会把一些对象复活,所以需要重新收集一次不可达对象,然后将复活的对象移入老年代中.

  1. 删除不可达对象
                        
                          static void
delete_garbage(PyThreadState *tstate, GCState *gcstate,
               PyGC_Head *collectable, PyGC_Head *old)
{
    assert(!_PyErr_Occurred(tstate));

    while (!gc_list_is_empty(collectable)) {
        PyGC_Head *gc = GC_NEXT(collectable);
        PyObject *op = FROM_GC(gc);

        _PyObject_ASSERT_WITH_MSG(op, Py_REFCNT(op) > 0,
                                  "refcount is too small");

        if (gcstate->debug & DEBUG_SAVEALL) {
            assert(gcstate->garbage != NULL);
            if (PyList_Append(gcstate->garbage, op) < 0) {
                _PyErr_Clear(tstate);
            }
        }
        else {
            inquiry clear;
            if ((clear = Py_TYPE(op)->tp_clear) != NULL) {
                Py_INCREF(op);
                (void) clear(op);
                if (_PyErr_Occurred(tstate)) {
                    _PyErr_WriteUnraisableMsg("in tp_clear of",
                                              (PyObject*)Py_TYPE(op));
                }
                Py_DECREF(op);
            }
        }
        if (GC_NEXT(collectable) == gc) {
            /* object is still alive, move it, it may die later */
            gc_clear_collecting(gc);
            gc_list_move(gc, old);
        }
    }
}

                        
                      

其中的逻辑也简单,遍历最终不可达列表,然后调用每个对象的 tp_clear 函数。调用后,如果对象可以被释放,则也会从GC列表中移除。所以在后面有一个判断 if (GC_NEXT(collectable) == gc) ,也就是该对象还没有被移除,这种情况则清除该对象的收集中标记,然后移入老年代中.

  1. finalizers 列表中的对象移入老年代中
                        
                          static void
handle_legacy_finalizers(PyThreadState *tstate,
                         GCState *gcstate,
                         PyGC_Head *finalizers, PyGC_Head *old)
{
    assert(!_PyErr_Occurred(tstate));
    assert(gcstate->garbage != NULL);

    PyGC_Head *gc = GC_NEXT(finalizers);
    for (; gc != finalizers; gc = GC_NEXT(gc)) {
        PyObject *op = FROM_GC(gc);

        if ((gcstate->debug & DEBUG_SAVEALL) || has_legacy_finalizer(op)) {
            if (PyList_Append(gcstate->garbage, op) < 0) {
                _PyErr_Clear(tstate);
                break;
            }
        }
    }

    gc_list_merge(finalizers, old);
}

                        
                      

所以说,定义了__del__的对象,有可能出现无法回收的情况。需要仔细编码.

总结

python的垃圾回收主要用到了 。

  1. 引用计数
  2. 标记清除
  3. 分代回收

其中分代回收步骤为 。

  1. 将年轻代的对象移动到指定回收代的列表后。
  2. 遍历回收代列表,将对象设置为收集中 PREV_MASK_COLLECTING 标记,然后将引用计数复制一份到_gc_prev中
  3. 然后遍历每个对象中的每个元素,如果这个元素也是可GC对象,并且也有收集中标记,则将_gc_prev中的计数值减1
  4. 再遍历回收代列表,判断_gc_prev计数值是否为0,
    1. 如果为0,则标记为不可达,然后移动到不可达列表中。
    2. 如果不为0,则遍历该对象的元素,如果该元素已经标记为清除,就把该元素移动到原回收代列表中。(也就是父对象仍然可达,则子对象也可达)。然后清除该对象的收集中标记。
  5. 遍历不可达列表,清除不可达标记,判断是否定义了__del__函数,如果有,则将清除收集中标记,并移入 finalizers 列表中。
  6. 遍历 finalizers 列表的每个对象,判断对象中的元素是否是可GC对象,并且有收集中标记,将该元素清除收集中标记,移入 finalizers 列表中。
  7. 遍历不可达列表, 处理弱引用
  8. 遍历不可达列表的每个对象,调用对象的 tp_finalize 函数,如果没有则跳过。
  9. 遍历不可达列表,将复活对象移到老年代列表中,其他对象移动到仍然不可达列表 final_unreachable
  10. 最后遍历 final_unreachable 列表,为每个对象调用 tp_clear 函数
    1. 如果真的可以删除,则把自己从对应GC列表中摘除
    2. 如果还不能删除,则清除对象的收集中标记,对象重新加入老年代中。
  11. finalizers 列表中的每个对象重新加入老年代列表中。

例子

说到这里好像还没有具体分析环引用的情况 。

                        
                          import sys
import gc


def a():
    lst1 = []
    lst2 = []

    lst1.append(lst2)
    lst2.append(lst1)

    print("lst1 refcnt: {}".format(sys.getrefcount(lst1)))
    print("lst2 refcnt: {}".format(sys.getrefcount(lst2)))

before_collect_cnt = gc.collect(2)
a()
after_collect_cnt = gc.collect(2)

print("before({}), after({})".format(before_collect_cnt, after_collect_cnt))

                        
                      

在笔者的电脑上输出 。

                        
                          hejs@ubuntu:~$ python main.py
lst1 refcnt: 3
lst2 refcnt: 3
before(0), after(2)

                        
                      

可以看到,在执行a函数时,lst1和lst2的引用计数为2(因为sys.getrefcount也会引用一次,所以输出的值是真实计数+1)。 当a函数调用结束后,由于函数内的lst1、lst2变量解除了引用,所以此时两个列表的计数值就为1了。出现环引用,无法释放。 这个时候就轮到标记清楚和分代回收解决了.

  1. 首先会将第0、1代的元素移到第2代上。因为 gc.collect(2)
  2. 然后遍历第2代列表,为每个对象设置收集中标记,将对象的真实计数复制到_gc_prev中。
  3. 再遍历第2代列表,判断对象的子元素是否也是 可GC对象、也有收集中标记,如果有则将该元素计数值减1。
    1. 此时 lst1、lst2的_gc_prev计数值都为0
  4. 然后将_gc_prev计数值为0的对象移入不可达列表中。
  5. 因为listobject没有__del__函数,也没有 tp_finalize 函数,所以直接到第10步,调用 tp_clear 函数。
                        
                          static int _list_clear(PyListObject *a)
{
    Py_ssize_t i;
    PyObject **item = a->ob_item;
    if (item != NULL) {
        i = Py_SIZE(a);
        Py_SET_SIZE(a, 0);
        a->ob_item = NULL;
        a->allocated = 0;
        while (--i >= 0) {
            Py_XDECREF(item[i]);
        }
        PyMem_FREE(item);
    }
    /* Never fails; the return value can be ignored.
       Note that there is no guarantee that the list is actually empty
       at this point, because XDECREF may have populated it again! */
    return 0;
}

                        
                      

也就是会为每个元素的引用计数减1。从之前分析可知,当计数减为0时,会调用对象的 tp_dealloc 函数,再看看listobject的 tp_dealloc 实现.

                        
                          static void
list_dealloc(PyListObject *op)
{
    Py_ssize_t i;
    PyObject_GC_UnTrack(op);
    Py_TRASHCAN_BEGIN(op, list_dealloc)
    if (op->ob_item != NULL) {
        i = Py_SIZE(op);
        while (--i >= 0) {
            Py_XDECREF(op->ob_item[i]);
        }
        PyMem_FREE(op->ob_item);
    }
    if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))
        free_list[numfree++] = op;
    else
        Py_TYPE(op)->tp_free((PyObject *)op);
    Py_TRASHCAN_END
}

                        
                      

首先会调用 PyObject_GC_UnTrack ,就是将该对象从GC链表中摘除。然后再遍历子元素,将子元素的计数减1。计数减为0时,又会调用对象的 tp_dealloc 函数.

此番调用下来,lst1和lst2的计数都会被减为0,都会从GC链表中摘除,并且都能释放。解除了环引用.

最后此篇关于Python垃圾回收的文章就讲到这里了,如果你想了解更多关于Python垃圾回收的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com