@@ -472,16 +472,14 @@ validate_list_is_aging(PyGC_Head *head)
472472/* Set all gc_refs = ob_refcnt. After this, gc_refs is > 0 and
473473 * PREV_MASK_COLLECTING bit is set for all objects in containers.
474474 */
475- static Py_ssize_t
475+ static void
476476update_refs (PyGC_Head * containers )
477477{
478478 PyGC_Head * next ;
479479 PyGC_Head * gc = GC_NEXT (containers );
480- Py_ssize_t size = 0 ;
481480
482481 while (gc != containers ) {
483482 next = GC_NEXT (gc );
484- size ++ ;
485483 /* Move any object that might have become immortal to the
486484 * permanent generation as the reference count is not accurately
487485 * reflecting the actual number of live references to this object
@@ -514,7 +512,6 @@ update_refs(PyGC_Head *containers)
514512 gc = next ;
515513 }
516514
517- return size ;
518515}
519516
520517/* A traversal callback for subtract_refs. */
@@ -1122,25 +1119,6 @@ clear_freelists(PyInterpreterState *interp)
11221119 _PyContext_ClearFreeList (interp );
11231120}
11241121
1125- // Show stats for objects in each generations
1126- static void
1127- show_stats_each_generations (GCState * gcstate )
1128- {
1129- char buf [100 ];
1130- size_t pos = 0 ;
1131-
1132- for (int i = 0 ; i < NUM_GENERATIONS && pos < sizeof (buf ); i ++ ) {
1133- pos += PyOS_snprintf (buf + pos , sizeof (buf )- pos ,
1134- " %zd" ,
1135- gc_list_size (GEN_HEAD (gcstate , i )));
1136- }
1137-
1138- PySys_FormatStderr (
1139- "gc: objects in each generation:%s\n"
1140- "gc: objects in permanent generation: %zd\n" ,
1141- buf , gc_list_size (& gcstate -> permanent_generation .head ));
1142- }
1143-
11441122/* Deduce which objects among "base" are unreachable from outside the list
11451123 and move them to 'unreachable'. The process consist in the following steps:
11461124
@@ -1168,15 +1146,15 @@ flag set but it does not clear it to skip unnecessary iteration. Before the
11681146flag is cleared (for example, by using 'clear_unreachable_mask' function or
11691147by a call to 'move_legacy_finalizers'), the 'unreachable' list is not a normal
11701148list and we can not use most gc_list_* functions for it. */
1171- static inline Py_ssize_t
1149+ static inline void
11721150deduce_unreachable (PyGC_Head * base , PyGC_Head * unreachable ) {
11731151 validate_list (base , collecting_clear_unreachable_clear );
11741152 /* Using ob_refcnt and gc_refs, calculate which objects in the
11751153 * container set are reachable from outside the set (i.e., have a
11761154 * refcount greater than 0 when all the references within the
11771155 * set are taken into account).
11781156 */
1179- Py_ssize_t size = update_refs (base ); // gc_prev is used for gc_refs
1157+ update_refs (base ); // gc_prev is used for gc_refs
11801158 subtract_refs (base );
11811159
11821160 /* Leave everything reachable from outside base in base, and move
@@ -1218,7 +1196,6 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
12181196 move_unreachable (base , unreachable ); // gc_prev is pointer again
12191197 validate_list (base , collecting_clear_unreachable_clear );
12201198 validate_list (unreachable , collecting_set_unreachable_set );
1221- return size ;
12221199}
12231200
12241201/* Handle objects that may have resurrected after a call to 'finalize_garbage', moving
@@ -1263,22 +1240,28 @@ gc_collect_region(PyThreadState *tstate,
12631240 int untrack ,
12641241 struct gc_collection_stats * stats );
12651242
1266- static void
1243+ static Py_ssize_t
12671244make_space0 (PyGC_Head * list )
12681245{
1246+ Py_ssize_t size = 0 ;
12691247 PyGC_Head * gc ;
12701248 for (gc = GC_NEXT (list ); gc != list ; gc = GC_NEXT (gc )) {
12711249 gc -> _gc_prev &= ~_PyGC_PREV_MASK_OLD_SPACE_1 ;
1250+ size ++ ;
12721251 }
1252+ return size ;
12731253}
12741254
1275- static void
1255+ static Py_ssize_t
12761256make_space1 (PyGC_Head * list )
12771257{
1258+ Py_ssize_t size = 0 ;
12781259 PyGC_Head * gc ;
12791260 for (gc = GC_NEXT (list ); gc != list ; gc = GC_NEXT (gc )) {
12801261 gc -> _gc_prev |= _PyGC_PREV_MASK_OLD_SPACE_1 ;
1262+ size ++ ;
12811263 }
1264+ return size ;
12821265}
12831266
12841267static void
@@ -1293,7 +1276,6 @@ static void
12931276gc_collect_young (PyThreadState * tstate ,
12941277 struct gc_collection_stats * stats )
12951278{
1296- assert (stats -> survivors == 0 );
12971279 GCState * gcstate = & tstate -> interp -> gc ;
12981280 PyGC_Head * young = & gcstate -> young .head ;
12991281 PyGC_Head * aging = & gcstate -> old [gcstate -> aging_space ].head ;
@@ -1311,25 +1293,26 @@ gc_collect_young(PyThreadState *tstate,
13111293 gc_list_init (& survivors );
13121294 gc_collect_region (tstate , young , & survivors , UNTRACK_TUPLES , stats );
13131295 validate_old (gcstate );
1296+ Py_ssize_t surivivor_count = 0 ;
13141297 if (gcstate -> aging_space ) {
1315- make_space0 (& survivors );
13161298 /* objects in aging space have bit set, so we set it here */
1317- make_space1 (& survivors );
1299+ surivivor_count = make_space1 (& survivors );
13181300 }
1319- #ifdef GC_DEBUG
13201301 else {
13211302 PyGC_Head * gc ;
13221303 for (gc = GC_NEXT (& survivors ); gc != & survivors ; gc = GC_NEXT (gc )) {
1304+ #ifdef GC_DEBUG
13231305 assert ((gc -> _gc_prev & _PyGC_PREV_MASK_OLD_SPACE_1 ) == 0 );
1306+ #endif
1307+ surivivor_count ++ ;
13241308 }
13251309 }
1326- #endif
13271310 validate_old (gcstate );
13281311 gc_list_merge (& survivors , aging );
13291312 validate_old (gcstate );
13301313 gcstate -> young .count = 0 ;
13311314 gcstate -> old [gcstate -> aging_space ].count ++ ;
1332- gcstate -> incremental_gc_progress -= stats -> survivors ;
1315+ gcstate -> incremental_gc_progress -= surivivor_count ;
13331316 add_stats (gcstate , 0 , stats );
13341317}
13351318
@@ -1343,13 +1326,14 @@ gc_collect_aging(PyThreadState *tstate,
13431326 PyGC_Head * aging = & gcstate -> old [gcstate -> aging_space ].head ;
13441327 PyGC_Head * oldest = & gcstate -> old [gcstate -> aging_space ^1 ].head ;
13451328 /* Need to tag all objects as part of oldest generation */
1329+ Py_ssize_t size ;
13461330 if (gcstate -> aging_space ) {
13471331 /* No need to tag young objects */
1348- make_space0 (aging );
1332+ size = make_space0 (aging );
13491333 }
13501334 else {
13511335 make_space1 (young );
1352- make_space1 (aging );
1336+ size = make_space1 (aging );
13531337 }
13541338 /* merge young in pending before collecting */
13551339 gc_list_merge (young , aging );
@@ -1359,7 +1343,7 @@ gc_collect_aging(PyThreadState *tstate,
13591343 gc_collect_region (tstate , aging , oldest ,
13601344 UNTRACK_TUPLES , stats );
13611345 validate_old (gcstate );
1362- gcstate -> incremental_gc_progress += stats -> survivors + stats -> collected ;
1346+ gcstate -> incremental_gc_progress += size ;
13631347 add_stats (gcstate , 1 , stats );
13641348}
13651349
@@ -1373,18 +1357,19 @@ IS_IN_OLDEST(PyGC_Head *head, uintptr_t aging_space)
13731357struct container_and_flag {
13741358 PyGC_Head * container ;
13751359 uintptr_t aging_space ;
1376- uintptr_t size ;
13771360};
13781361
13791362/* A traversal callback for adding to container) */
13801363static int
13811364visit_add_to_container (PyObject * op , void * arg )
13821365{
13831366 OBJECT_STAT_INC (object_visits );
1367+ struct container_and_flag * cf = (struct container_and_flag * )arg ;
1368+ uintptr_t oldest_flag = cf -> aging_space ^ _PyGC_PREV_MASK_OLD_SPACE_1 ;
13841369 if (_PyObject_IS_GC (op )) {
1385- struct container_and_flag * cf = (struct container_and_flag * )arg ;
13861370 PyGC_Head * gc = AS_GC (op );
1387- if (_PyObject_GC_IS_TRACKED (op ) && IS_IN_OLDEST (gc , cf -> aging_space )) {
1371+ if (_PyObject_GC_IS_TRACKED (op ) &&
1372+ (gc -> _gc_prev & _PyGC_PREV_MASK_OLD_SPACE_1 ) == oldest_flag ) {
13881373 assert (!_Py_IsImmortal (op ));
13891374 gc -> _gc_prev ^= _PyGC_PREV_MASK_OLD_SPACE_1 ;
13901375 gc_list_move (gc , cf -> container );
@@ -1431,11 +1416,6 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14311416 if (gcstate -> incremental_gc_progress >= 0 ) {
14321417 return ;
14331418 }
1434- /* We rely on stats for controlling work done in incremental GC,
1435- * so they need to zeroed */
1436- assert (stats -> survivors == 0 );
1437- assert (stats -> collected == 0 );
1438- assert (stats -> uncollectable == 0 );
14391419 PyGC_Head * oldest = & gcstate -> old [gcstate -> aging_space ^1 ].head ;
14401420 PyGC_Head * aging = & gcstate -> old [gcstate -> aging_space ].head ;
14411421 validate_old (gcstate );
@@ -1448,7 +1428,9 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14481428 validate_old (gcstate );
14491429 if (gc_list_is_empty (oldest )) {
14501430 if (gc_list_is_empty (aging )) {
1451- gcstate -> incremental_gc_progress = gcstate -> young .threshold ;
1431+ if (gcstate -> incremental_gc_progress < gcstate -> young .threshold ) {
1432+ gcstate -> incremental_gc_progress = gcstate -> young .threshold ;
1433+ }
14521434 return ;
14531435 }
14541436 /* aging is empty, so swap with oldest */
@@ -1460,6 +1442,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14601442 validate_old (gcstate );
14611443 while (region_size < work_to_do ) {
14621444 if (gc_list_is_empty (oldest )) {
1445+ gcstate -> incremental_gc_progress = 0 ;
14631446 break ;
14641447 }
14651448 PyGC_Head * gc = _PyGCHead_NEXT (oldest );
@@ -1473,10 +1456,8 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14731456 gc_collect_region (tstate , & increment , aging , 0 , stats );
14741457 validate_old (gcstate );
14751458 assert (gc_list_is_empty (& increment ));
1476- Py_ssize_t work_done = stats -> collected + stats -> survivors + stats -> uncollectable ;
1477- //fprintf(stderr, "Work done %d\n", (int)work_done);
1478- gcstate -> incremental_gc_progress += work_done ;
1479- //fprintf(stderr, "Incremental GC complete\n");
1459+ gcstate -> incremental_gc_progress += region_size ;
1460+ assert (gcstate -> incremental_gc_progress >= 0 );
14801461 validate_old (gcstate );
14811462 add_stats (gcstate , 1 , stats );
14821463}
@@ -1508,7 +1489,7 @@ gc_collect_full(PyThreadState *tstate,
15081489 gcstate -> old [0 ].count = 0 ;
15091490 gcstate -> old [1 ].count = 0 ;
15101491
1511- gcstate -> incremental_gc_progress += stats -> survivors + stats -> collected ;
1492+ gcstate -> incremental_gc_progress = gcstate -> young . threshold * 2 ;
15121493 clear_freelists (tstate -> interp );
15131494 validate_old (gcstate );
15141495 add_stats (gcstate , 2 , stats );
@@ -1536,7 +1517,7 @@ gc_collect_region(PyThreadState *tstate,
15361517
15371518 validate_list (to , collecting_clear_unreachable_clear );
15381519
1539- Py_ssize_t processed = deduce_unreachable (from , & unreachable );
1520+ deduce_unreachable (from , & unreachable );
15401521 if (untrack & UNTRACK_TUPLES ) {
15411522 untrack_tuples (from );
15421523 }
@@ -1609,7 +1590,6 @@ gc_collect_region(PyThreadState *tstate,
16091590 handle_legacy_finalizers (tstate , gcstate , & finalizers , to );
16101591 validate_list (to , collecting_clear_unreachable_clear );
16111592
1612- stats -> survivors = processed - m - n ;
16131593 stats -> collected = m ;
16141594 stats -> uncollectable = n ;
16151595 validate_old (gcstate );
@@ -2169,7 +2149,7 @@ gc.unfreeze
21692149
21702150Unfreeze all objects in the permanent generation.
21712151
2172- Put all objects in the permanent generation back into old[0] .
2152+ Put all objects in the permanent generation back into oldest generation .
21732153[clinic start generated code]*/
21742154
21752155static PyObject *
0 commit comments