大师兄的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)
}