diff -r d9c555047fcc Modules/gcmodule.c --- a/Modules/gcmodule.c Sun Dec 14 12:09:40 2008 +0100 +++ b/Modules/gcmodule.c Sun Dec 14 17:11:16 2008 +0100 @@ -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 exponential 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,14 @@ 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) { + /* If there are many long-lived objects, use MvL's + heuristic to do full collections less frequently. + See issue #4074. + */ + if (i == NUM_GENERATIONS - 1 + && long_lived_total > 10000 + && long_lived_pending < long_lived_total / 4) + continue; n = collect(i); break; }