大师兄的Python源码学习笔记(五十七): Python的内存管理机制(十二)

大师兄的Python源码学习笔记(五十六): Python的内存管理机制(十一)
大师兄的Python源码学习笔记(五十八): Python的内存管理机制(十三)

五、Python中的垃圾收集

3. 标记——清除方法
3.3 垃圾回收
  • 要回收unreachable链表中的垃圾对象,就必须打破对象间的循环引用,也就是前面提到的标记——清除分代收集
Modules/gcmodule.c

/* Break reference cycles by clearing the containers involved.  This is
 * tricky business as the lists can be changing and we don't know which
 * objects may be freed.  It is possible I screwed something up here.
 */
static void
delete_garbage(PyGC_Head *collectable, PyGC_Head *old)
{
    inquiry clear;

    while (!gc_list_is_empty(collectable)) {
        PyGC_Head *gc = collectable->gc.gc_next;
        PyObject *op = FROM_GC(gc);

        if (_PyRuntime.gc.debug & DEBUG_SAVEALL) {
            PyList_Append(_PyRuntime.gc.garbage, op);
        }
        else {
            if ((clear = Py_TYPE(op)->tp_clear) != NULL) {
                Py_INCREF(op);
                clear(op);
                Py_DECREF(op);
            }
        }
        if (collectable->gc.gc_next == gc) {
            /* object is still alive, move it, it may die later */
            gc_list_move(gc, old);
            _PyGCHead_SET_REFS(gc, GC_REACHABLE);
        }
    }
}
Modules/gcmodule.c

static int
gc_list_is_empty(PyGC_Head *list)
{
    return (list->gc.gc_next == list);
}
Include/objimpl.h

#define _PyGCHead_SET_REFS(g, v) do { \
    (g)->gc.gc_refs = ((g)->gc.gc_refs & ~_PyGC_REFS_MASK) \
        | (((size_t)(v)) << _PyGC_REFS_SHIFT);             \
    } while (0)
  • 这里会直接调整对象的ob_refcnt,直到unreachable链表中的每一个对象的ob_refcnt都为0,引发对象销毁。
  • 这其中还会调用container对象的类型对象中的tp_clear操作,这个操作会调整container对象中每个引用所用的对象的引用计数值,从而完成打破循环的最终目标, 例如listobject对象:
Objects/listobject.c

static int
_list_clear(PyListObject *a)
{
   Py_ssize_t i;
   PyObject **item = a->ob_item;
   if (item != NULL) {
       /* Because XDECREF can recursively invoke operations on
          this list, we make it empty first. */
       i = Py_SIZE(a);
       Py_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;
}
  • 可以看到,在delete_garbage中,有一些unreachable链表中的对象会被重新送回到reachable链表,也就是old参数中。
  • 这是由于在进行clear动作时,如果成功进行,通常一个对象会把自己从垃圾收集机制维护的collectable链表中摘除。
  • 如果对象在clear动作时,没有将自己从collectable中摘除,这表示对象认为自己还不能被销毁,所以Python需要将这种对象放回reachable链表中。
  • 现在可以在观察下unreachable链表中的listobject是如何被回收的:
  • delete_garbage中,加入首先处理list1,调用其list_clear,会减少list2的引用计数。
  • 这将导致list2的ob_refcnt为0,引发对象销毁动作,会调用list2的list_dealloc
  • 这会将list2从可收集对象列表中摘除。
  • 然后如果list_clear所做的,会调整list2所引用的所有对象的引用计数
  • 这个动作立即影响到list1,并使其引用计数变为0,所以list1的销毁动作也被触发。
  • 这样list1和list2都被安全的回收了。
Objects/listobject.c

static void
list_dealloc(PyListObject *op)
{
    Py_ssize_t i;
    PyObject_GC_UnTrack(op);
    Py_TRASHCAN_SAFE_BEGIN(op)
    if (op->ob_item != NULL) {
        /* Do it backwards, for Christian Tismer.
           There's a simple test case where somehow this reduces
           thrashing when a *very* large list is created and
           immediately deleted. */
        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_SAFE_END(op)
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容