https://github.com/python/cpython/commit/8615062fd580934fc431746f64b1ac7ee6ef8e88
commit: 8615062fd580934fc431746f64b1ac7ee6ef8e88
branch: main
author: Donghee Na <[email protected]>
committer: corona10 <[email protected]>
date: 2026-07-08T12:27:32Z
summary:
gh-153083: Defer GC tracking of an array to the end of construction. (gh-153284)
files:
A Misc/NEWS.d/next/Library/2026-07-08-01-47-15.gh-issue-153083.XbKZCp.rst
M Include/internal/pycore_object.h
M Modules/arraymodule.c
diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index 98880cd83780dca..307ecdbaaefcea2 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -872,7 +872,8 @@ static inline int _PyType_SUPPORTS_WEAKREFS(PyTypeObject
*type) {
return (type->tp_weaklistoffset != 0);
}
-extern PyObject* _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems);
+// Export for 'array' shared extension.
+PyAPI_FUNC(PyObject*) _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t
nitems);
PyAPI_FUNC(PyObject *) _PyType_NewManagedObject(PyTypeObject *type);
extern PyTypeObject* _PyType_CalculateMetaclass(PyTypeObject *, PyObject *);
diff --git
a/Misc/NEWS.d/next/Library/2026-07-08-01-47-15.gh-issue-153083.XbKZCp.rst
b/Misc/NEWS.d/next/Library/2026-07-08-01-47-15.gh-issue-153083.XbKZCp.rst
new file mode 100644
index 000000000000000..b6d96851e17342f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-08-01-47-15.gh-issue-153083.XbKZCp.rst
@@ -0,0 +1,2 @@
+Defer GC tracking of an :class:`array.array` to the end of its construction.
+Patch by Donghee Na.
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index 2b1373dbe6eb461..68486c66575933a 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -14,6 +14,7 @@
#include "pycore_floatobject.h" // _PY_FLOAT_BIG_ENDIAN
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_moduleobject.h" // _PyModule_GetState()
+#include "pycore_object.h" // _PyObject_GC_TRACK()
#include "pycore_tuple.h" // _PyTuple_FromPairSteal
#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
@@ -752,7 +753,8 @@ class array.array "arrayobject *" "ArrayType"
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a5c29edf59f176a3]*/
static PyObject *
-newarrayobject(PyTypeObject *type, Py_ssize_t size, const struct arraydescr
*descr)
+newarrayobject_untracked(PyTypeObject *type, Py_ssize_t size,
+ const struct arraydescr *descr)
{
arrayobject *op;
size_t nbytes;
@@ -767,7 +769,7 @@ newarrayobject(PyTypeObject *type, Py_ssize_t size, const
struct arraydescr *des
return PyErr_NoMemory();
}
nbytes = size * descr->itemsize;
- op = (arrayobject *) type->tp_alloc(type, 0);
+ op = (arrayobject *) _PyType_AllocNoTrack(type, 0);
if (op == NULL) {
return NULL;
}
@@ -789,6 +791,16 @@ newarrayobject(PyTypeObject *type, Py_ssize_t size, const
struct arraydescr *des
return (PyObject *) op;
}
+static PyObject *
+newarrayobject(PyTypeObject *type, Py_ssize_t size, const struct arraydescr
*descr)
+{
+ PyObject *op = newarrayobject_untracked(type, size, descr);
+ if (op != NULL) {
+ _PyObject_GC_TRACK(op);
+ }
+ return op;
+}
+
static PyObject *
getarrayitem(PyObject *op, Py_ssize_t i)
{
@@ -990,13 +1002,14 @@ array_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t
ihigh)
ihigh = ilow;
else if (ihigh > Py_SIZE(a))
ihigh = Py_SIZE(a);
- np = (arrayobject *) newarrayobject(state->ArrayType, ihigh - ilow,
a->ob_descr);
+ np = (arrayobject *) newarrayobject_untracked(state->ArrayType, ihigh -
ilow, a->ob_descr);
if (np == NULL)
return NULL;
if (ihigh > ilow) {
memcpy(np->ob_item, a->ob_item + ilow * a->ob_descr->itemsize,
(ihigh-ilow) * a->ob_descr->itemsize);
}
+ _PyObject_GC_TRACK(np);
return (PyObject *)np;
}
@@ -1067,7 +1080,7 @@ array_concat(PyObject *op, PyObject *bb)
return PyErr_NoMemory();
}
size = Py_SIZE(a) + Py_SIZE(b);
- np = (arrayobject *) newarrayobject(state->ArrayType, size, a->ob_descr);
+ np = (arrayobject *) newarrayobject_untracked(state->ArrayType, size,
a->ob_descr);
if (np == NULL) {
return NULL;
}
@@ -1078,6 +1091,7 @@ array_concat(PyObject *op, PyObject *bb)
memcpy(np->ob_item + Py_SIZE(a)*a->ob_descr->itemsize,
b->ob_item, Py_SIZE(b)*b->ob_descr->itemsize);
}
+ _PyObject_GC_TRACK(np);
return (PyObject *)np;
#undef b
}
@@ -1095,16 +1109,19 @@ array_repeat(PyObject *op, Py_ssize_t n)
return PyErr_NoMemory();
}
Py_ssize_t size = array_length * n;
- arrayobject* np = (arrayobject *) newarrayobject(state->ArrayType, size,
a->ob_descr);
+ arrayobject* np = (arrayobject *)
newarrayobject_untracked(state->ArrayType, size, a->ob_descr);
if (np == NULL)
return NULL;
- if (size == 0)
+ if (size == 0) {
+ _PyObject_GC_TRACK(np);
return (PyObject *)np;
+ }
const Py_ssize_t oldbytes = array_length * a->ob_descr->itemsize;
const Py_ssize_t newbytes = oldbytes * n;
_PyBytes_RepeatBuffer(np->ob_item, newbytes, a->ob_item, oldbytes);
+ _PyObject_GC_TRACK(np);
return (PyObject *)np;
}
@@ -2666,17 +2683,17 @@ array_subscr(PyObject *op, PyObject *item)
return newarrayobject(state->ArrayType, 0, self->ob_descr);
}
else if (step == 1) {
- PyObject *result = newarrayobject(state->ArrayType,
- slicelength, self->ob_descr);
+ PyObject *result = newarrayobject_untracked(state->ArrayType,
slicelength, self->ob_descr);
if (result == NULL)
return NULL;
memcpy(((arrayobject *)result)->ob_item,
self->ob_item + start * itemsize,
slicelength * itemsize);
+ _PyObject_GC_TRACK(result);
return result;
}
else {
- result = newarrayobject(state->ArrayType, slicelength,
self->ob_descr);
+ result = newarrayobject_untracked(state->ArrayType, slicelength,
self->ob_descr);
if (!result) return NULL;
ar = (arrayobject*)result;
@@ -2688,6 +2705,7 @@ array_subscr(PyObject *op, PyObject *item)
itemsize);
}
+ _PyObject_GC_TRACK(result);
return result;
}
}
@@ -2973,7 +2991,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject
*kwds)
else
len = 0;
- a = newarrayobject(type, len, descr);
+ a = newarrayobject_untracked(type, len, descr);
if (a == NULL) {
Py_XDECREF(it);
return NULL;
@@ -3039,6 +3057,8 @@ array_new(PyTypeObject *type, PyObject *args, PyObject
*kwds)
}
Py_DECREF(it);
}
+ // Track only once fully built.
+ _PyObject_GC_TRACK(a);
return a;
}
}
@@ -3140,7 +3160,7 @@ static PyType_Slot array_slots[] = {
{Py_tp_methods, array_methods},
{Py_tp_members, array_members},
{Py_tp_getset, array_getsets},
- {Py_tp_alloc, PyType_GenericAlloc},
+ {Py_tp_alloc, _PyType_AllocNoTrack},
{Py_tp_new, array_new},
{Py_tp_traverse, _PyObject_VisitType},
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]