@@ -29,9 +29,8 @@ typedef struct _gc_runtime_state GCState;
2929#define GC_NEXT _PyGCHead_NEXT
3030#define GC_PREV _PyGCHead_PREV
3131
32- // update_refs() set this bit for all objects in current generation.
33- // subtract_refs() and move_unreachable() uses this to distinguish
34- // visited object is in GCing or not.
32+ // Reference initialization marks objects in the current generation.
33+ // Reference subtraction and reachability use this bit to recognize them.
3534//
3635// move_unreachable() removes this flag from reachable objects.
3736// Only unreachable objects have this flag.
@@ -168,15 +167,14 @@ During a collection, _gc_prev is temporary used for gc_refs, and the gc list
168167is singly linked until _gc_prev is restored.
169168
170169gc_refs
171- At the start of a collection, update_refs() copies the true refcount
172- to gc_refs, for each object in the generation being collected.
173- subtract_refs() then adjusts gc_refs so that it equals the number of
174- times an object is referenced directly from outside the generation
175- being collected.
170+ The true refcount is copied to gc_refs and reduced by references from
171+ objects in the generation being collected. The remaining count is the
172+ number of direct references from outside that generation. Full collections
173+ may initialize and subtract these counts in one traversal.
176174
177175PREV_MASK_COLLECTING
178- Objects in generation being collected are marked PREV_MASK_COLLECTING in
179- update_refs() .
176+ Reference initialization marks objects in the generation being collected
177+ with PREV_MASK_COLLECTING .
180178
181179
182180_gc_next values
@@ -456,14 +454,52 @@ visit_decref(PyObject *op, void *parent)
456454 return 0 ;
457455}
458456
457+ /* Subtract a GC reference, initializing and marking the referent on first visit.
458+ *
459+ * This visitor is used only when every tracked mortal object it may reach
460+ * belongs to the collection. Non-GC, untracked, and immortal referents remain
461+ * unchanged.
462+ *
463+ * Arguments:
464+ * - `op`: the object referenced by the traversed parent.
465+ * - `parent`: the traversed owner used by debug reference validation.
466+ *
467+ * Return zero, as required by traversal callbacks.
468+ */
469+ static int
470+ gc_decref_and_mark_collecting (PyObject * op , void * parent )
471+ {
472+ OBJECT_STAT_INC (object_visits );
473+ _PyObject_ASSERT (_PyObject_CAST (parent ), !_PyObject_IsFreed (op ));
474+
475+ if (!_PyObject_IS_GC (op )) {
476+ return 0 ;
477+ }
478+
479+ PyGC_Head * gc = AS_GC (op );
480+ if (gc_is_collecting (gc )) {
481+ gc_decref (gc );
482+ return 0 ;
483+ }
484+
485+ if (!_PyObject_GC_IS_TRACKED (op ) || _Py_IsImmortal (op )) {
486+ return 0 ;
487+ }
488+ Py_ssize_t refs = Py_REFCNT (op );
489+ _PyObject_ASSERT (op , refs != 0 );
490+ gc_reset_refs (gc , refs - 1 );
491+ return 0 ;
492+ }
493+
459494int
460495_PyGC_VisitStackRef (_PyStackRef * ref , visitproc visit , void * arg )
461496{
462497 // This is a bit tricky! We want to ignore stackrefs with embedded
463498 // refcounts when computing the incoming references, but otherwise treat
464499 // them like normal.
465500 assert (!PyStackRef_IsTaggedInt (* ref ));
466- if (!PyStackRef_RefcountOnObject (* ref ) && (visit == visit_decref )) {
501+ if (!PyStackRef_RefcountOnObject (* ref ) &&
502+ (visit == visit_decref || visit == gc_decref_and_mark_collecting )) {
467503 return 0 ;
468504 }
469505 Py_VISIT (PyStackRef_AsPyObjectBorrow (* ref ));
@@ -501,6 +537,60 @@ subtract_refs(PyGC_Head *containers)
501537 }
502538}
503539
540+ /* Initialize and subtract references in one traversal of all ordinary generations.
541+ *
542+ * Mortal candidates retain adjusted gc_refs. Dynamic immortal candidates become
543+ * untracked without changing other candidates' temporary counts.
544+ *
545+ * Prerequisites:
546+ * - This is the initial pass of a full collection, not a resurrection recheck.
547+ * - `containers` is a valid list containing all ordinary generations with no masks set.
548+ * - The permanent generation is empty.
549+ * - `_PyInterpreterState_MayHaveForeignGcObjects()` is false.
550+ *
551+ * Arguments:
552+ * - `containers`: tracked objects belonging to the initial main interpreter.
553+ *
554+ * Return the number of mortal collection candidates.
555+ */
556+ static Py_ssize_t
557+ update_and_subtract_refs (PyGC_Head * containers )
558+ {
559+ Py_ssize_t candidates = 0 ;
560+ PyGC_Head * previous = containers ;
561+ PyGC_Head * gc = GC_NEXT (containers );
562+
563+ while (gc != containers ) {
564+ PyGC_Head * next = GC_NEXT (gc );
565+ PyObject * op = FROM_GC (gc );
566+
567+ if (!gc_is_collecting (gc )) {
568+ if (_Py_IsImmortal (op )) {
569+ assert (!_Py_IsStaticImmortal (op ));
570+ // The next node may already hold gc_refs instead of a predecessor,
571+ // so ordinary untracking would corrupt its count.
572+ _PyGCHead_SET_NEXT (previous , next );
573+ gc -> _gc_prev &= _PyGC_PREV_MASK_FINALIZED ;
574+ gc -> _gc_next = 0 ;
575+ _PyInterpreterState_GET ()-> gc .heap_size -- ;
576+ gc = next ;
577+ continue ;
578+ }
579+ gc_reset_refs (gc , Py_REFCNT (op ));
580+ _PyObject_ASSERT (op , gc_get_refs (gc ) != 0 );
581+ }
582+
583+ candidates ++ ;
584+ (void )Py_TYPE (op )-> tp_traverse (op , gc_decref_and_mark_collecting , op );
585+ previous = gc ;
586+ gc = next ;
587+ }
588+
589+ // The tail must remain valid until reachability restores predecessor links.
590+ containers -> _gc_prev = (uintptr_t )previous ;
591+ return candidates ;
592+ }
593+
504594/* A traversal callback for move_unreachable. */
505595static int
506596visit_reachable (PyObject * op , void * arg )
@@ -1185,6 +1275,8 @@ show_stats_each_generations(GCState *gcstate)
11851275/* Deduce which objects among "base" are unreachable from outside the list
11861276 and move them to 'unreachable'. The process consist in the following steps:
11871277
1278+ The first two steps may be combined for the initial pass of a full collection.
1279+
118812801. Copy all reference counts to a different field (gc_prev is used to hold
11891281 this copy to save memory).
119012822. Traverse all objects in "base" and visit all referred objects using
@@ -1204,22 +1296,39 @@ show_stats_each_generations(GCState *gcstate)
12041296 * The "unreachable" list must be uninitialized (this function calls
12051297 gc_list_init over 'unreachable').
12061298
1299+ * "full_collection_pass" means this is the initial pass over all ordinary generations.
1300+ Resurrection rechecks pass false even during a full collection.
1301+
1302+ * "reachable" must point to storage for the number of survivors that remain tracked.
1303+
1304+ * The return value is the number of mortal collection candidates.
1305+
12071306IMPORTANT: This function leaves 'unreachable' with the NEXT_MASK_UNREACHABLE
12081307flag set but it does not clear it to skip unnecessary iteration. Before the
12091308flag is cleared (for example, by using 'clear_unreachable_mask' function or
12101309by a call to 'move_legacy_finalizers'), the 'unreachable' list is not a normal
12111310list and we can not use most gc_list_* functions for it. */
12121311static inline Py_ssize_t
12131312deduce_unreachable (PyGC_Head * base , PyGC_Head * unreachable ,
1214- Py_ssize_t * reachable ) {
1313+ bool full_collection_pass , Py_ssize_t * reachable ) {
12151314 validate_list (base , collecting_clear_unreachable_clear );
12161315 /* Using ob_refcnt and gc_refs, calculate which objects in the
12171316 * container set are reachable from outside the set (i.e., have a
12181317 * refcount greater than 0 when all the references within the
12191318 * set are taken into account).
12201319 */
1221- Py_ssize_t candidates = update_refs (base ); // gc_prev is used for gc_refs
1222- subtract_refs (base );
1320+ // Lazy initialization can touch any tracked mortal referent. Use it only
1321+ // when all such objects belong to this initial full collection.
1322+ Py_ssize_t candidates ;
1323+ if (full_collection_pass &&
1324+ gc_list_is_empty (& get_gc_state ()-> permanent_generation .head ) &&
1325+ !_PyInterpreterState_MayHaveForeignGcObjects ()) {
1326+ candidates = update_and_subtract_refs (base );
1327+ }
1328+ else {
1329+ candidates = update_refs (base ); // gc_prev is used for gc_refs
1330+ subtract_refs (base );
1331+ }
12231332
12241333 /* Leave everything reachable from outside base in base, and move
12251334 * everything else (in base) to unreachable.
@@ -1290,7 +1399,7 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
12901399 // removed so we can skip the expense of clearing the flag.
12911400 PyGC_Head * resurrected = unreachable ;
12921401 Py_ssize_t reachable ;
1293- deduce_unreachable (resurrected , still_unreachable , & reachable );
1402+ deduce_unreachable (resurrected , still_unreachable , false, & reachable );
12941403 clear_unreachable_mask (still_unreachable );
12951404
12961405 // Move the resurrected objects to the old generation for future collection.
@@ -1597,7 +1706,7 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
15971706 validate_list (old , collecting_clear_unreachable_clear );
15981707
15991708 Py_ssize_t reachable ;
1600- stats .candidates = deduce_unreachable (young , & unreachable , & reachable );
1709+ stats .candidates = deduce_unreachable (young , & unreachable , generation == NUM_GENERATIONS - 1 , & reachable );
16011710
16021711 /* Move reachable objects to next generation. */
16031712 if (young != old ) {
0 commit comments