# HG changeset patch # User Antoine Pitrou # Date 1229556899 -3600 diff -r 99507aabe77a -r 5989f5396f02 Modules/gcmodule.c --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -63,6 +63,22 @@ static PyObject *gc_str = NULL; /* Python string used to look for __del__ attribute. */ static PyObject *delstr = NULL; +/* This is the number of objects who survived the last full collection. It + approximates the number of long lived objects tracked by the GC. */ +static Py_ssize_t long_lived_total = 0; +/* This is the number of objects who survived the last "before-full" + collection, and awaiting to undergo a full collection for the first time. + + When there are many long-lived objects, the ratio + long_lived_pending / long_lived_total + is used to decide when to trigger a full collection + (in collect_generations()). Before Python 2.7, not doing so could lead + to quadratic performance degradation when keeping lots (e.g. millions) + of GC-enabled objects in memory. + +*/ +static Py_ssize_t long_lived_pending = 0; + /* set for debugging information */ #define DEBUG_STATS (1<<0) /* print collection statistics */ #define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */ @@ -817,8 +833,16 @@ collect(int generation) move_unreachable(young, &unreachable); /* Move reachable objects to next generation. */ - if (young != old) + if (young != old) { + if (generation == NUM_GENERATIONS - 2) { + long_lived_pending += gc_list_size(young); + } gc_list_merge(young, old); + } + else { + long_lived_pending = 0; + long_lived_total = gc_list_size(young); + } /* All objects in unreachable are trash, but objects reachable from * finalizers can't safely be deleted. Python programmers should take @@ -918,6 +942,12 @@ collect_generations(void) * generations younger than it will be collected. */ for (i = NUM_GENERATIONS-1; i >= 0; i--) { if (generations[i].count > generations[i].threshold) { + /* Avoid quadratic performance degradation in number + of tracked objects. See issue #4074. + */ + if (i == NUM_GENERATIONS - 1 + && long_lived_pending < long_lived_total / 4) + continue; n = collect(i); break; }