3131
3232/*** Global GC state ***/
3333
34+ struct gc_generation {
35+ PyGC_Head head ;
36+ int threshold ; /* collection threshold */
37+ int count ; /* count of allocations or collections of younger
38+ generations */
39+ };
40+
41+ #define NUM_GENERATIONS 3
42+ #define GEN_HEAD (n ) (&generations[n].head)
43+
3444/* linked lists of container objects */
35- PyGC_Head _PyGC_generation0 = {{& _PyGC_generation0 , & _PyGC_generation0 , 0 }};
36- static PyGC_Head generation1 = {{& generation1 , & generation1 , 0 }};
37- static PyGC_Head generation2 = {{& generation2 , & generation2 , 0 }};
38- static int generation = 0 ; /* current generation being collected */
45+ static struct gc_generation generations [NUM_GENERATIONS ] = {
46+ /* PyGC_Head, threshold, count */
47+ {{{GEN_HEAD (0 ), GEN_HEAD (0 ), 0 }}, 700 , 0 },
48+ {{{GEN_HEAD (1 ), GEN_HEAD (1 ), 0 }}, 10 , 0 },
49+ {{{GEN_HEAD (2 ), GEN_HEAD (2 ), 0 }}, 10 , 0 },
50+ };
3951
40- /* collection frequencies, XXX tune these */
41- static int enabled = 1 ; /* automatic collection enabled? */
42- static int threshold0 = 700 ; /* net new containers before collection */
43- static int threshold1 = 10 ; /* generation0 collections before collecting 1 */
44- static int threshold2 = 10 ; /* generation1 collections before collecting 2 */
52+ PyGC_Head * _PyGC_generation0 = GEN_HEAD (0 );
4553
46- /* net new objects allocated since last collection */
47- static int allocated ;
54+ static int enabled = 1 ; /* automatic collection enabled? */
4855
4956/* true if we are currently running the collector */
5057static int collecting ;
@@ -81,6 +88,12 @@ gc_list_init(PyGC_Head *list)
8188 list -> gc .gc_next = list ;
8289}
8390
91+ static int
92+ gc_list_is_empty (PyGC_Head * list )
93+ {
94+ return (list -> gc .gc_next == list );
95+ }
96+
8497static void
8598gc_list_append (PyGC_Head * node , PyGC_Head * list )
8699{
@@ -101,8 +114,7 @@ gc_list_remove(PyGC_Head *node)
101114static void
102115gc_list_move (PyGC_Head * from , PyGC_Head * to )
103116{
104- if (from -> gc .gc_next == from ) {
105- /* empty from list */
117+ if (gc_list_is_empty (from )) {
106118 gc_list_init (to );
107119 }
108120 else {
@@ -119,7 +131,7 @@ static void
119131gc_list_merge (PyGC_Head * from , PyGC_Head * to )
120132{
121133 PyGC_Head * tail ;
122- if (from -> gc . gc_next != from ) {
134+ if (! gc_list_is_empty ( from ) ) {
123135 tail = to -> gc .gc_prev ;
124136 tail -> gc .gc_next = from -> gc .gc_next ;
125137 tail -> gc .gc_next -> gc .gc_prev = tail ;
@@ -330,7 +342,7 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
330342{
331343 inquiry clear ;
332344
333- while (unreachable -> gc . gc_next != unreachable ) {
345+ while (! gc_list_is_empty ( unreachable ) ) {
334346 PyGC_Head * gc = unreachable -> gc .gc_next ;
335347 PyObject * op = FROM_GC (gc );
336348 if (debug & DEBUG_SAVEALL ) {
@@ -354,23 +366,45 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
354366/* This is the main function. Read this to understand how the
355367 * collection process works. */
356368static long
357- collect (PyGC_Head * young , PyGC_Head * old )
369+ collect (int generation )
358370{
371+ int i ;
359372 long n = 0 ;
360373 long m = 0 ;
374+ PyGC_Head * young ; /* the generation we are examining */
375+ PyGC_Head * old ; /* next older generation */
361376 PyGC_Head reachable ;
362377 PyGC_Head unreachable ;
363378 PyGC_Head finalizers ;
364379 PyGC_Head * gc ;
365380
366381 if (debug & DEBUG_STATS ) {
367- PySys_WriteStderr (
368- "gc: collecting generation %d...\n"
369- "gc: objects in each generation: %ld %ld %ld\n" ,
370- generation ,
371- gc_list_size (& _PyGC_generation0 ),
372- gc_list_size (& generation1 ),
373- gc_list_size (& generation2 ));
382+ PySys_WriteStderr ("gc: collecting generation %d...\n" ,
383+ generation );
384+ PySys_WriteStderr ("gc: objects in each generation:" );
385+ for (i = 0 ; i < NUM_GENERATIONS ; i ++ ) {
386+ PySys_WriteStderr (" %ld" , gc_list_size (GEN_HEAD (i )));
387+ }
388+ PySys_WriteStderr ("\n" );
389+ }
390+
391+ /* update collection and allocation counters */
392+ if (generation + 1 < NUM_GENERATIONS )
393+ generations [generation + 1 ].count += 1 ;
394+ for (i = 0 ; i <= generation ; i ++ )
395+ generations [generation ].count = 0 ;
396+
397+ /* merge younger generations with one we are currently collecting */
398+ for (i = 0 ; i < generation ; i ++ ) {
399+ gc_list_merge (GEN_HEAD (i ), GEN_HEAD (generation ));
400+ }
401+
402+ /* handy references */
403+ young = GEN_HEAD (generation );
404+ if (generation < NUM_GENERATIONS - 1 ) {
405+ old = GEN_HEAD (generation + 1 );
406+ } else {
407+ old = GEN_HEAD (NUM_GENERATIONS - 1 );
374408 }
375409
376410 /* Using ob_refcnt and gc_refs, calculate which objects in the
@@ -449,41 +483,22 @@ collect(PyGC_Head *young, PyGC_Head *old)
449483 PyErr_WriteUnraisable (gc_str );
450484 Py_FatalError ("unexpected exception during garbage collection" );
451485 }
452- allocated = 0 ;
453486 return n + m ;
454487}
455488
456489static long
457490collect_generations (void )
458491{
459- static long collections0 = 0 ;
460- static long collections1 = 0 ;
492+ int i ;
461493 long n = 0 ;
462494
463-
464- if (collections1 > threshold2 ) {
465- generation = 2 ;
466- gc_list_merge (& _PyGC_generation0 , & generation2 );
467- gc_list_merge (& generation1 , & generation2 );
468- if (generation2 .gc .gc_next != & generation2 ) {
469- n = collect (& generation2 , & generation2 );
470- }
471- collections1 = 0 ;
472- }
473- else if (collections0 > threshold1 ) {
474- generation = 1 ;
475- collections1 ++ ;
476- gc_list_merge (& _PyGC_generation0 , & generation1 );
477- if (generation1 .gc .gc_next != & generation1 ) {
478- n = collect (& generation1 , & generation2 );
479- }
480- collections0 = 0 ;
481- }
482- else {
483- generation = 0 ;
484- collections0 ++ ;
485- if (_PyGC_generation0 .gc .gc_next != & _PyGC_generation0 ) {
486- n = collect (& _PyGC_generation0 , & generation1 );
495+ /* Find the oldest generation (higest numbered) where the count
496+ * exceeds the threshold. Objects in the that generation and
497+ * generations younger than it will be collected. */
498+ for (i = NUM_GENERATIONS - 1 ; i >= 0 ; i -- ) {
499+ if (generations [i ].count > generations [i ].threshold ) {
500+ n = collect (i );
501+ break ;
487502 }
488503 }
489504 return n ;
@@ -562,10 +577,7 @@ gc_collect(PyObject *self, PyObject *args)
562577 }
563578 else {
564579 collecting = 1 ;
565- generation = 2 ;
566- gc_list_merge (& _PyGC_generation0 , & generation2 );
567- gc_list_merge (& generation1 , & generation2 );
568- n = collect (& generation2 , & generation2 );
580+ n = collect (NUM_GENERATIONS - 1 );
569581 collecting = 0 ;
570582 }
571583
@@ -624,9 +636,16 @@ static char gc_set_thresh__doc__[] =
624636static PyObject *
625637gc_set_thresh (PyObject * self , PyObject * args )
626638{
627- if (!PyArg_ParseTuple (args , "i|ii:set_threshold" , & threshold0 ,
628- & threshold1 , & threshold2 ))
639+ int i ;
640+ if (!PyArg_ParseTuple (args , "i|ii:set_threshold" ,
641+ & generations [0 ].threshold ,
642+ & generations [1 ].threshold ,
643+ & generations [2 ].threshold ))
629644 return NULL ;
645+ for (i = 2 ; i < NUM_GENERATIONS ; i ++ ) {
646+ /* generations higher than 2 get the same threshold */
647+ generations [i ].threshold = generations [2 ].threshold ;
648+ }
630649
631650 Py_INCREF (Py_None );
632651 return Py_None ;
@@ -644,7 +663,10 @@ gc_get_thresh(PyObject *self, PyObject *args)
644663 if (!PyArg_ParseTuple (args , ":get_threshold" )) /* no args */
645664 return NULL ;
646665
647- return Py_BuildValue ("(iii)" , threshold0 , threshold1 , threshold2 );
666+ return Py_BuildValue ("(iii)" ,
667+ generations [0 ].threshold ,
668+ generations [1 ].threshold ,
669+ generations [2 ].threshold );
648670}
649671
650672static int
@@ -683,12 +705,13 @@ Return the list of objects that directly refer to any of objs.";
683705static PyObject *
684706gc_get_referrers (PyObject * self , PyObject * args )
685707{
708+ int i ;
686709 PyObject * result = PyList_New (0 );
687- if (!( gc_referrers_for ( args , & _PyGC_generation0 , result ) &&
688- gc_referrers_for (args , & generation1 , result ) &&
689- gc_referrers_for ( args , & generation2 , result ))) {
690- Py_DECREF ( result ) ;
691- return NULL ;
710+ for ( i = 0 ; i < NUM_GENERATIONS ; i ++ ) {
711+ if (!( gc_referrers_for (args , GEN_HEAD ( i ) , result ))) {
712+ Py_DECREF ( result );
713+ return NULL ;
714+ }
692715 }
693716 return result ;
694717}
@@ -719,6 +742,7 @@ append_objects(PyObject *py_list, PyGC_Head *gc_list)
719742static PyObject *
720743gc_get_objects (PyObject * self , PyObject * args )
721744{
745+ int i ;
722746 PyObject * result ;
723747
724748 if (!PyArg_ParseTuple (args , ":get_objects" )) /* check no args */
@@ -727,11 +751,11 @@ gc_get_objects(PyObject *self, PyObject *args)
727751 if (result == NULL ) {
728752 return NULL ;
729753 }
730- if ( append_objects ( result , & _PyGC_generation0 ) ||
731- append_objects (result , & generation1 ) ||
732- append_objects (result , & generation2 )) {
733- Py_DECREF ( result ) ;
734- return NULL ;
754+ for ( i = 0 ; i < NUM_GENERATIONS ; i ++ ) {
755+ if ( append_objects (result , GEN_HEAD ( i ))) {
756+ Py_DECREF (result );
757+ return NULL ;
758+ }
735759 }
736760 return result ;
737761}
@@ -854,14 +878,14 @@ _PyObject_GC_Malloc(size_t basicsize)
854878 if (g == NULL )
855879 return (PyObject * )PyErr_NoMemory ();
856880 g -> gc .gc_next = NULL ;
857- allocated ++ ;
858- if (allocated > threshold0 &&
881+ generations [ 0 ]. count ++ ; /* number of allocated GC objects */
882+ if (generations [ 0 ]. count > generations [ 0 ]. threshold &&
859883 enabled &&
860- threshold0 &&
884+ generations [ 0 ]. threshold &&
861885 !collecting &&
862886 !PyErr_Occurred ()) {
863887 collecting = 1 ;
864- collect_generations ();
888+ collect_generations ();
865889 collecting = 0 ;
866890 }
867891 op = FROM_GC (g );
@@ -919,8 +943,8 @@ PyObject_GC_Del(void *op)
919943 PyGC_Head * g = AS_GC (op );
920944 if (g -> gc .gc_next != NULL )
921945 gc_list_remove (g );
922- if (allocated > 0 ) {
923- allocated -- ;
946+ if (generations [ 0 ]. count > 0 ) {
947+ generations [ 0 ]. count -- ;
924948 }
925949 PyObject_FREE (g );
926950#else
0 commit comments