原文連結:http://whosemario.github.io/2016/01/03/python-PyType-GenericAlloc/
為什麼要寫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雙向連結清單中