Skip to content

Commit 3f88e55

Browse files
committed
Forward-port generational GC to 3.15
1 parent e998eb9 commit 3f88e55

File tree

12 files changed

+1077
-2061
lines changed

12 files changed

+1077
-2061
lines changed

Doc/library/gc.rst

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ The :mod:`!gc` module provides the following functions:
4545
Calling ``gc.collect(0)`` will perform a GC collection on the young generation.
4646

4747
Calling ``gc.collect(1)`` will perform a GC collection on the young generation
48-
and an increment of the old generation.
48+
and generation 1.
4949

50-
Calling ``gc.collect(2)`` or ``gc.collect()`` performs a full collection
50+
Calling ``gc.collect(2)`` or ``gc.collect()`` performs a full collection.
5151

5252
The free lists maintained for a number of built-in types are cleared
5353
whenever a full collection or collection of the highest generation (2)
@@ -57,9 +57,6 @@ The :mod:`!gc` module provides the following functions:
5757
The effect of calling ``gc.collect()`` while the interpreter is already
5858
performing a collection is undefined.
5959

60-
.. versionchanged:: 3.14
61-
``generation=1`` performs an increment of collection.
62-
6360

6461
.. function:: set_debug(flags)
6562

@@ -80,15 +77,12 @@ The :mod:`!gc` module provides the following functions:
8077
returned. If *generation* is not ``None``, return only the objects as follows:
8178

8279
* 0: All objects in the young generation
83-
* 1: No objects, as there is no generation 1 (as of Python 3.14)
80+
* 1: All objects in generation 1
8481
* 2: All objects in the old generation
8582

8683
.. versionchanged:: 3.8
8784
New *generation* parameter.
8885

89-
.. versionchanged:: 3.14
90-
Generation 1 is removed
91-
9286
.. audit-event:: gc.get_objects generation gc.get_objects
9387

9488
.. function:: get_stats()
@@ -124,33 +118,27 @@ The :mod:`!gc` module provides the following functions:
124118
Set the garbage collection thresholds (the collection frequency). Setting
125119
*threshold0* to zero disables collection.
126120

127-
The GC classifies objects into two generations depending on whether they have
128-
survived a collection. New objects are placed in the young generation. If an
129-
object survives a collection it is moved into the old generation.
130-
131-
In order to decide when to run, the collector keeps track of the number of object
132-
allocations and deallocations since the last collection. When the number of
133-
allocations minus the number of deallocations exceeds *threshold0*, collection
134-
starts. For each collection, all the objects in the young generation and some
135-
fraction of the old generation is collected.
121+
The GC classifies objects into three generations depending on how many
122+
collection sweeps they have survived. New objects are placed in the young
123+
generation (generation ``0``). If an object survives a collection it is moved
124+
into the next older generation. Since generation ``2`` is the oldest
125+
generation, objects in that generation remain there after a collection.
126+
127+
In order to decide when to run, the collector keeps track of the number of
128+
object allocations and deallocations since the last collection. When the
129+
number of allocations minus the number of deallocations exceeds *threshold0*,
130+
collection starts. Initially only generation ``0`` is examined. If
131+
generation ``0`` has been examined more than *threshold1* times since
132+
generation ``1`` has been examined, then generation ``1`` is examined as
133+
well. With generation ``2``, things are a bit more complicated; see
134+
`Garbage collector design <https://devguide.python.org/garbage_collector>`_
135+
for more information.
136136

137137
In the free-threaded build, the increase in process memory usage is also
138-
checked before running the collector. If the memory usage has not increased
138+
checked before running the collector. If the memory usage has not increased
139139
by 10% since the last collection and the net number of object allocations
140140
has not exceeded 40 times *threshold0*, the collection is not run.
141141

142-
The fraction of the old generation that is collected is **inversely** proportional
143-
to *threshold1*. The larger *threshold1* is, the slower objects in the old generation
144-
are collected.
145-
For the default value of 10, 1% of the old generation is scanned during each collection.
146-
147-
*threshold2* is ignored.
148-
149-
See `Garbage collector design <https://devguide.python.org/garbage_collector>`_ for more information.
150-
151-
.. versionchanged:: 3.14
152-
*threshold2* is ignored
153-
154142

155143
.. function:: get_count()
156144

Doc/whatsnew/3.14.rst

Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Interpreter improvements:
108108
* :ref:`A new type of interpreter <whatsnew314-tail-call-interpreter>`
109109
* :ref:`Free-threaded mode improvements <whatsnew314-free-threaded-cpython>`
110110
* :ref:`Improved error messages <whatsnew314-improved-error-messages>`
111-
* :ref:`Incremental garbage collection <whatsnew314-incremental-gc>`
111+
* :ref:`Generational garbage collection <whatsnew314-incremental-gc>`
112112

113113
Significant improvements in the standard library:
114114

@@ -955,26 +955,22 @@ when a module is imported) will still emit the syntax warning.
955955

956956
.. _whatsnew314-incremental-gc:
957957

958-
Incremental garbage collection
959-
------------------------------
960-
961-
The cycle garbage collector is now incremental.
962-
This means that maximum pause times are reduced
963-
by an order of magnitude or more for larger heaps.
958+
Generational garbage collection
959+
-------------------------------
964960

965-
There are now only two generations: young and old.
966-
When :func:`gc.collect` is not called directly, the
967-
GC is invoked a little less frequently. When invoked, it
968-
collects the young generation and an increment of the
969-
old generation, instead of collecting one or more generations.
961+
The cycle garbage collector uses three generations in the default build:
962+
generation 0, generation 1, and generation 2.
970963

971-
The behavior of :func:`!gc.collect` changes slightly:
964+
The behavior of :func:`!gc.collect` matches the traditional generational
965+
collector:
972966

973-
* ``gc.collect(1)``: Performs an increment of garbage collection,
974-
rather than collecting generation 1.
975-
* Other calls to :func:`!gc.collect` are unchanged.
967+
* ``gc.collect(0)``: Collect generation 0.
968+
* ``gc.collect(1)``: Collect generations 0 and 1.
969+
* ``gc.collect(2)`` or ``gc.collect()``: Perform a full collection.
976970

977-
(Contributed by Mark Shannon in :gh:`108362`.)
971+
Likewise, :func:`!gc.get_objects(1)` returns objects in generation 1, and
972+
:func:`!gc.set_threshold` and :func:`!gc.get_threshold` continue to use all
973+
three threshold values.
978974

979975

980976
Default interactive shell
@@ -2203,36 +2199,11 @@ difflib
22032199
gc
22042200
--
22052201

2206-
* The new :ref:`incremental garbage collector <whatsnew314-incremental-gc>`
2207-
means that maximum pause times are reduced
2208-
by an order of magnitude or more for larger heaps.
2209-
2210-
Because of this optimization, the meaning of the results of
2211-
:meth:`~gc.get_threshold` and :meth:`~gc.set_threshold` have changed,
2212-
along with :meth:`~gc.get_count` and :meth:`~gc.get_stats`.
2213-
2214-
- For backwards compatibility, :meth:`~gc.get_threshold` continues to return
2215-
a three-item tuple.
2216-
The first value is the threshold for young collections, as before;
2217-
the second value determines the rate at which the old collection is scanned
2218-
(the default is 10, and higher values mean that the old collection
2219-
is scanned more slowly).
2220-
The third value is now meaningless and is always zero.
2221-
2222-
- :meth:`~gc.set_threshold` now ignores any items after the second.
2223-
2224-
- :meth:`~gc.get_count` and :meth:`~gc.get_stats` continue to return
2225-
the same format of results.
2226-
The only difference is that instead of the results referring to
2227-
the young, aging and old generations,
2228-
the results refer to the young generation
2229-
and the aging and collecting spaces of the old generation.
2230-
2231-
In summary, code that attempted to manipulate the behavior of the cycle GC
2232-
may not work exactly as intended, but it is very unlikely to be harmful.
2233-
All other code will work just fine.
2234-
2235-
(Contributed by Mark Shannon in :gh:`108362`.)
2202+
* The :ref:`garbage collector uses three generations <whatsnew314-incremental-gc>`.
2203+
In particular, :func:`gc.collect(1)` collects generations 0 and 1,
2204+
:func:`gc.get_objects(1)` returns objects in generation 1, and
2205+
:func:`gc.set_threshold` and :func:`gc.get_threshold` continue to use all
2206+
three threshold values.
22362207

22372208

22382209
io
@@ -3273,12 +3244,11 @@ Changes in the Python API
32733244
Wrap it in :func:`staticmethod` if you want to preserve the old behavior.
32743245
(Contributed by Serhiy Storchaka and Dominykas Grigonis in :gh:`121027`.)
32753246

3276-
* The :ref:`garbage collector is now incremental <whatsnew314-incremental-gc>`,
3277-
which means that the behavior of :func:`gc.collect` changes slightly:
3278-
3279-
* ``gc.collect(1)``: Performs an increment of garbage collection,
3280-
rather than collecting generation 1.
3281-
* Other calls to :func:`!gc.collect` are unchanged.
3247+
* The :ref:`garbage collector uses three generations <whatsnew314-incremental-gc>`.
3248+
In particular, :func:`gc.collect(1)` collects generations 0 and 1,
3249+
:func:`gc.get_objects(1)` returns objects in generation 1, and
3250+
:func:`gc.set_threshold` and :func:`gc.get_threshold` continue to use all
3251+
three threshold values.
32823252

32833253
* The :func:`locale.nl_langinfo` function now temporarily sets the ``LC_CTYPE``
32843254
locale in some cases.

Include/internal/pycore_gc.h

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,6 @@ static inline void _PyGC_CLEAR_FINALIZED(PyObject *op) {
207207

208208
extern void _Py_ScheduleGC(PyThreadState *tstate);
209209

210-
#ifndef Py_GIL_DISABLED
211-
extern void _Py_TriggerGC(struct _gc_runtime_state *gcstate);
212-
#endif
213-
214210

215211
/* Tell the GC to track this object.
216212
*
@@ -220,7 +216,7 @@ extern void _Py_TriggerGC(struct _gc_runtime_state *gcstate);
220216
* ob_traverse method.
221217
*
222218
* Internal note: interp->gc.generation0->_gc_prev doesn't have any bit flags
223-
* because it's not object header. So we don't use _PyGCHead_PREV() and
219+
* because it's not an object header. So we don't use _PyGCHead_PREV() and
224220
* _PyGCHead_SET_PREV() for it to avoid unnecessary bitwise operations.
225221
*
226222
* See also the public PyObject_GC_Track() function.
@@ -245,18 +241,12 @@ static inline void _PyObject_GC_TRACK(
245241
filename, lineno, __func__);
246242

247243
struct _gc_runtime_state *gcstate = &_PyInterpreterState_GET()->gc;
248-
PyGC_Head *generation0 = &gcstate->young.head;
249-
PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev);
244+
PyGC_Head *generation0 = gcstate->generation0;
245+
PyGC_Head *last = (PyGC_Head *)(generation0->_gc_prev);
250246
_PyGCHead_SET_NEXT(last, gc);
251247
_PyGCHead_SET_PREV(gc, last);
252-
uintptr_t not_visited = 1 ^ gcstate->visited_space;
253-
gc->_gc_next = ((uintptr_t)generation0) | not_visited;
248+
_PyGCHead_SET_NEXT(gc, generation0);
254249
generation0->_gc_prev = (uintptr_t)gc;
255-
gcstate->young.count++; /* number of tracked GC objects */
256-
gcstate->heap_size++;
257-
if (gcstate->young.count > gcstate->young.threshold) {
258-
_Py_TriggerGC(gcstate);
259-
}
260250
#endif
261251
}
262252

@@ -291,11 +281,6 @@ static inline void _PyObject_GC_UNTRACK(
291281
_PyGCHead_SET_PREV(next, prev);
292282
gc->_gc_next = 0;
293283
gc->_gc_prev &= _PyGC_PREV_MASK_FINALIZED;
294-
struct _gc_runtime_state *gcstate = &_PyInterpreterState_GET()->gc;
295-
if (gcstate->young.count > 0) {
296-
gcstate->young.count--;
297-
}
298-
gcstate->heap_size--;
299284
#endif
300285
}
301286

Include/internal/pycore_interp_structs.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,16 @@ struct _gc_runtime_state {
243243
/* Is automatic collection enabled? */
244244
int enabled;
245245
int debug;
246-
/* linked lists of container objects */
246+
247+
/* Generational GC state used in GIL builds. */
248+
struct gc_generation generations[NUM_GENERATIONS];
249+
PyGC_Head *generation0;
250+
struct gc_generation_stats generation_stats_gen[NUM_GENERATIONS];
251+
252+
/* Incremental/free-threaded GC state. */
247253
struct gc_generation young;
248254
struct gc_generation old[2];
255+
249256
/* a permanent generation which won't be collected */
250257
struct gc_generation permanent_generation;
251258
struct gc_stats *generation_stats;
@@ -265,7 +272,6 @@ struct _gc_runtime_state {
265272
int visited_space;
266273
int phase;
267274

268-
#ifdef Py_GIL_DISABLED
269275
/* This is the number of objects that survived the last full
270276
collection. It approximates the number of long lived objects
271277
tracked by the GC.
@@ -278,6 +284,7 @@ struct _gc_runtime_state {
278284
the first time. */
279285
Py_ssize_t long_lived_pending;
280286

287+
#ifdef Py_GIL_DISABLED
281288
/* True if gc.freeze() has been used. */
282289
int freeze_active;
283290

Include/internal/pycore_runtime_init.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ extern PyTypeObject _PyExc_MemoryError;
130130
}, \
131131
.gc = { \
132132
.enabled = 1, \
133+
.generations = { \
134+
{ .threshold = 2000, }, \
135+
{ .threshold = 10, }, \
136+
{ .threshold = 10, }, \
137+
}, \
133138
.young = { .threshold = 2000, }, \
134139
.old = { \
135140
{ .threshold = 10, }, \

0 commit comments

Comments
 (0)