天天看點

Python源碼解析——PyType_GenericAlloc

原文連結: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,看一下具體初始化了哪些東西

  1. ob_size = nitems 初始化數量
  2. ob_type = type 初始化對應的PyObjectType
  3. ob_refcnt = 1 初始化引用計數為1
  4. 調用_Py_AddToAllObjects,将obj加入到refchain雙向連結清單中