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 PyObje