Skip to main content

Modules/gcmodule.c (part 5)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/gcmodule.c

This annotation covers the gc module's inspection and control functions. See python_gc4_detail for cyclic collection internals, finalizers, and generation promotion.

Map

LinesSymbolRole
1-80gc.get_referrersFind objects that refer to given objects
81-160gc.get_referentsGet objects directly referred to
161-240gc.is_trackedCheck if an object is in the GC
241-320gc.freeze / gc.unfreezeMove gen-2 objects out of GC reach
321-400gc.get_count / gc.get_thresholdQuery collection state

Reading

gc.get_referrers

// CPython: Modules/gcmodule.c:1280 gc_get_referrers
static PyObject *
gc_get_referrers(PyObject *self, PyObject *args)
{
/* For each GC-tracked object, check if it refers to any of args */
PyObject *result = PyList_New(0);
for (int g = 0; g < NUM_GENERATIONS; g++) {
PyGC_Head *gc = GC_NEXT(&generations[g].head);
while (gc != &generations[g].head) {
PyObject *op = FROM_GC(gc);
traverseproc traverse = Py_TYPE(op)->tp_traverse;
if (traverse(op, referrers_visit, args) != 0) {
PyList_Append(result, op);
}
gc = GC_NEXT(gc);
}
}
return result;
}

gc.get_referrers(obj) traverses all GC-tracked objects and returns those that directly reference obj. This is O(total GC objects) and can be slow. Used for debugging memory leaks.

gc.get_referents

// CPython: Modules/gcmodule.c:1340 gc_get_referents
static int
referents_visit(PyObject *obj, void *arg)
{
PyList_Append((PyObject *)arg, obj);
return 0;
}

static PyObject *
gc_get_referents(PyObject *self, PyObject *args)
{
PyObject *result = PyList_New(0);
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) {
PyObject *obj = PyTuple_GET_ITEM(args, i);
traverseproc traverse = Py_TYPE(obj)->tp_traverse;
if (traverse) traverse(obj, referents_visit, result);
}
return result;
}

gc.get_referents(obj) calls tp_traverse on obj and collects all directly referenced objects. Unlike gc.get_referrers, this is O(references from obj) not O(all objects).

gc.freeze

// CPython: Modules/gcmodule.c:1420 gc_freeze
static PyObject *
gc_freeze(PyObject *module, PyObject *Py_UNUSED(ignored))
{
/* Move all gen-0/1/2 objects to the permanent generation (gen 3).
Frozen objects are never collected; they survive process forks. */
for (int i = 0; i < NUM_GENERATIONS; i++) {
gc_list_merge(&generations[i].head, &permanent_generation.head);
generations[i].count = 0;
}
Py_RETURN_NONE;
}

gc.freeze() moves all tracked objects to a "permanent generation" that is never collected. Used before os.fork() to prevent the child from spending time collecting objects inherited from the parent. gc.unfreeze() moves them back.

gopy notes

gc.get_referrers is module/gc.GetReferrers in module/gc/module.go. It uses objects.Traverse to walk referents. gc.freeze is module/gc.Freeze which moves objects to a permanent set excluded from the GC sweep. gc.is_tracked checks objects.IsGCTracked.