PyObject Header Fields in CPython Explained

In CPython, every Python object is represented under the hood by a C structure whose foundation is PyObject. This article examines the internal layout of the PyObject structure header, breaking down the specific fields that facilitate CPython's reference counting, dynamic typing, and memory tracking mechanisms.

The Core PyObject Definition

In the CPython source code (specifically within Include/object.h), the base structure is defined as:

struct _object {
    _PyObject_HEAD_EXTRA
    union {
        Py_ssize_t ob_refcnt;
        #if SIZEOF_VOID_P > 4
        uint32_t ob_refcnt_split[2];
        #endif
    };
    PyTypeObject *ob_type;
};

This structure is standardized using the macro PyObject_HEAD. When fully expanded in a standard release build, PyObject consists of two primary fields, with additional fields enabled conditionally in specialized builds.


1. ob_refcnt (Reference Count)

CPython uses reference counting as its primary memory management strategy. The ob_refcnt field records how many active references currently point to the object.

In Python 3.12 and newer, ob_refcnt supports "immortal objects" (such as None, True, False, and small integers), where specific high-bit flags prevent standard increments and decrements, eliminating cache-line bouncing across multi-core systems.


2. ob_type (Type Pointer)

Because Python is dynamically typed, the object itself must store information about what type of data it represents. The ob_type pointer links the object instance to its corresponding type object (such as &PyLong_Type, &PyList_Type, or user-defined classes).

The type object contains:

When an operation like len(obj) or obj + other is executed, CPython inspects obj->ob_type to resolve the corresponding C function.


3. _PyObject_HEAD_EXTRA (Debug Fields)

In a standard release build, _PyObject_HEAD_EXTRA evaluates to empty code and consumes zero bytes. In debug configurations with Py_TRACE_REFS enabled, it inserts two pointer fields—_ob_next and _ob_prev.

These pointers place every allocated Python object into a global, doubly linked list of all active heap objects. This enables runtime heap inspection, leak detection, and debugging tools within CPython.


Extension: PyVarObject and ob_size

Variable-length objects (such as str, tuple, list, and bytes) extend PyObject using the PyVarObject structure, which includes PyObject_VAR_HEAD:

struct _varobject {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
};