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双向链表中