为什么要写PyType_GenericAlloc函数,原因是本人想写一篇关于创建Class的源码解析的博文,发现内容比较多,因此就打算分开解析里面比较重要的内容。
PyType_GenericAlloc用来做什么,从函数命名可以看出GenericAlloc说明是用于一般情况的内存空间分配,PyType说明是针对PyTypeObject进行内存空间分配的。
1.关于GC
Python有自己的GC逻辑,这里不多讲,主要想说明Python会维护需要GC的各种PyObject,因此Python在PyObject的头部又加入了一个PyGC_Head。
# 内存分布
|-------------|
| PyGC_Head |
|-------------|
| PyObject |
| |
| ... |
因此有一个函数_PyObject_GC_Malloc负责分配上述的内存空间。
PyObject *
_PyObject_GC_Malloc(size_t basicsize)
{
PyObject *op;
PyGC_Head *g;
if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head))
return PyErr_NoMemory();
g = (PyGC_Head *)PyObject_MALLOC(
sizeof(PyGC_Head) + basicsize);
...
op = FROM_GC(g);
return op;
}
2.PyType_GenericAlloc全景
PyObject *
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
{
PyObject *obj;
const size_t size = _PyObject_VAR_SIZE(type, nitems+1); [1]
/* note that we need to add one, for the sentinel */
if (PyType_IS_GC(type))
obj = _PyObject_GC_Malloc(size); [2]
else
obj = (PyObject *)PyObject_MALLOC(size);
if (obj == NULL)
return PyErr_NoMemory();
memset(obj, '\0', size);
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE)
Py_INCREF(type);
if (type->tp_itemsize == 0)
PyObject_INIT(obj, type);
else
(void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems); [3]
if (PyType_IS_GC(type))
_PyObject_GC_TRACK(obj);
return obj;
}
关注三点,[1]处,计算要创建的PyObject的大小(nitems为什么要加1需要再深入看一下)。
#define _PyObject_VAR_SIZE(typeobj, nitems) \
(size_t) \
( ( (typeobj)->tp_basicsize + \
(nitems)*(typeobj)->tp_itemsize + \
(SIZEOF_VOID_P - 1) \
) & ~(SIZEOF_VOID_P - 1) \
)
size的组成是tp_basicsize + nitems * tp_itemsize,后面加上SIZEOF_VOID_P - 1是为了内存对齐。
[2]处使用_PyObject_GC_Malloc进行真正的内存分配。
[3]处初始化PyObject,确切的说应该是PyVarObject,看一下具体初始化了哪些东西
- ob_size = nitems 初始化数量
- ob_type = type 初始化对应的PyObjectType
- ob_refcnt = 1 初始化引用计数为1
- 调用_Py_AddToAllObjects,将obj加入到refchain双向链表中