final patches from Neil Schemenauer for garbage collection
diff --git a/Include/objimpl.h b/Include/objimpl.h
index 860030f..003dfdf 100644
--- a/Include/objimpl.h
+++ b/Include/objimpl.h
@@ -203,7 +203,6 @@
 ( (type *) PyObject_InitVar( \
 	(PyVarObject *) PyObject_MALLOC( _PyObject_VAR_SIZE((typeobj),(n)) ),\
 	(typeobj), (n)) )
-#define PyObject_DEL(op) PyObject_FREE(op)
 
 /* This example code implements an object constructor with a custom
    allocator, where PyObject_New is inlined, and shows the important
@@ -234,11 +233,67 @@
    the 1st step is performed automatically for you, so in a C++ class
    constructor you would start directly with PyObject_Init/InitVar. */
 
+/*
+ * Garbage Collection Support
+ * ==========================
+ */
 
+/* To make a new object participate in garbage collection use
+   PyObject_{New, VarNew, Del} to manage the memory.  Set the type flag
+   Py_TPFLAGS_GC and define the type method tp_recurse.  You should also
+   add the method tp_clear if your object is mutable.  Include
+   PyGC_INFO_SIZE in the calculation of tp_basicsize.  Call
+   PyObject_GC_Init after the pointers followed by tp_recurse become
+   valid (usually just before returning the object from the allocation
+   method.  Call PyObject_GC_Fini before those pointers become invalid
+   (usually at the top of the deallocation method).  */
 
 #ifndef WITH_CYCLE_GC
-#define PyGC_INFO_SIZE 0
-#endif
+
+#define PyGC_HEAD_SIZE 0
+#define PyObject_GC_Init(op)
+#define PyObject_GC_Fini(op)
+#define PyObject_AS_GC(op) (op)
+#define PyObject_FROM_GC(op) (op)
+#define PyObject_DEL(op) PyObject_FREE(op)
+ 
+#else
+
+/* Add the object into the container set */
+extern DL_IMPORT(void) _PyGC_Insert Py_PROTO((PyObject *));
+
+/* Remove the object from the container set */
+extern DL_IMPORT(void) _PyGC_Remove Py_PROTO((PyObject *));
+
+#define PyObject_GC_Init(op) _PyGC_Insert((PyObject *)op)
+#define PyObject_GC_Fini(op) _PyGC_Remove((PyObject *)op)
+
+/* Structure *prefixed* to container objects participating in GC */ 
+typedef struct _gc_head {
+	struct _gc_head *gc_next;
+	struct _gc_head *gc_prev;
+	int gc_refs;
+} PyGC_Head;
+
+#define PyGC_HEAD_SIZE sizeof(PyGC_Head)
+
+/* Test if a type has a GC head */
+#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_GC)
+
+/* Test if an object has a GC head */
+#define PyObject_IS_GC(o) PyType_IS_GC((o)->ob_type)
+
+/* Get an object's GC head */
+#define PyObject_AS_GC(o) ((PyGC_Head *)(o)-1)
+
+/* Get the object given the PyGC_Head */
+#define PyObject_FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))
+
+#define PyObject_DEL(op) PyObject_FREE( PyObject_IS_GC(op) ? \
+					(ANY *)PyObject_AS_GC(op) : \
+					(ANY *)(op) )
+
+#endif /* WITH_CYCLE_GC */
 
 #ifdef __cplusplus
 }
diff --git a/Lib/test/output/test_gc b/Lib/test/output/test_gc
new file mode 100644
index 0000000..c385928
--- /dev/null
+++ b/Lib/test/output/test_gc
@@ -0,0 +1,11 @@
+test_gc
+list 0x831a754
+dict 0x831a754
+list 0x831a754
+tuple 0x831a734
+class 0x831a794
+<test_gc.A instance at 831a754>
+a <test_gc.A instance at 831a754>
+b <test_gc.B instance at 831a95c>
+dict 0x831a9bc
+func 0x831d9e4
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
new file mode 100644
index 0000000..72253f4
--- /dev/null
+++ b/Lib/test/test_gc.py
@@ -0,0 +1,100 @@
+import gc
+
+def test_list():
+    l = []
+    l.append(l)
+    print 'list 0x%x' % id(l)
+    gc.collect()
+    del l
+    assert gc.collect() == 1
+
+def test_dict():
+    d = {}
+    d[1] = d
+    print 'dict 0x%x' % id(d)
+    gc.collect()
+    del d
+    assert gc.collect() == 1
+
+def test_tuple():
+    l = []
+    t = (l,)
+    l.append(t)
+    print 'list 0x%x' % id(l)
+    print 'tuple 0x%x' % id(t)
+    gc.collect()
+    del t
+    del l
+    assert gc.collect() == 2
+
+def test_class():
+    class A:
+        pass
+    A.a = A
+    print 'class 0x%x' % id(A)
+    gc.collect()
+    del A
+    assert gc.collect() > 0
+
+def test_instance():
+    class A:
+        pass
+    a = A()
+    a.a = a
+    print repr(a)
+    gc.collect()
+    del a
+    assert gc.collect() > 0
+
+def test_method():
+    class A:
+        def __init__(self):
+            self.init = self.__init__
+    a = A()
+    gc.collect()
+    del a
+    assert gc.collect() > 0
+
+def test_finalizer():
+    class A:
+        def __del__(self): pass
+    class B:
+        pass
+    a = A()
+    a.a = a
+    id_a = id(a)
+    b = B()
+    b.b = b
+    print 'a', repr(a)
+    print 'b', repr(b)
+    gc.collect()
+    gc.garbage[:] = []
+    del a
+    del b
+    assert gc.collect() > 0
+    assert id(gc.garbage[0]) == id_a
+
+def test_function():
+    d = {}
+    exec("def f(): pass\n") in d
+    print 'dict 0x%x' % id(d)
+    print 'func 0x%x' % id(d['f'])
+    gc.collect()
+    del d
+    assert gc.collect() == 2
+
+
+def test_all():
+    debug = gc.get_debug()
+    gc.set_debug(gc.DEBUG_LEAK | gc.DEBUG_STATS)
+    test_list()
+    test_dict()
+    test_tuple()
+    test_class()
+    test_instance()
+    test_method()
+    test_finalizer()
+    test_function()
+    gc.set_debug(debug)
+
+test_all()
diff --git a/Modules/Setup.thread.in b/Modules/Setup.thread.in
index d532156..7360144 100644
--- a/Modules/Setup.thread.in
+++ b/Modules/Setup.thread.in
@@ -10,3 +10,6 @@
 # support threads.
 
 @USE_THREAD_MODULE@thread threadmodule.c
+
+# Garbage collection enabled with --with-cycle-gc
+@USE_GC_MODULE@gc gcmodule.c
diff --git a/Modules/cPickle.c b/Modules/cPickle.c
index 0d91936..f9ef78f 100644
--- a/Modules/cPickle.c
+++ b/Modules/cPickle.c
@@ -2892,7 +2892,7 @@
                 Py_DECREF(inst);
                 goto err;
               }
-
+              PyObject_GC_Init(inst);
               return (PyObject *)inst;
             }
           Py_DECREF(__getinitargs__);
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
new file mode 100644
index 0000000..8c0dc3d
--- /dev/null
+++ b/Modules/gcmodule.c
@@ -0,0 +1,679 @@
+/*
+ 
+  Reference Cycle Garbage Collection
+  ==================================
+
+  Neil Schemenauer <nascheme@enme.ucalgary.ca>
+
+  Based on a post on the python-dev list.  Ideas from Guido van Rossum,
+  Eric Tiedemann, and various others.
+
+  http://www.enme.calgary.ca/~nascheme/python/gc.html
+  http://www.python.org/pipermail/python-dev/2000-March/003869.html
+  http://www.python.org/pipermail/python-dev/2000-March/004010.html
+  http://www.python.org/pipermail/python-dev/2000-March/004022.html
+
+  For a highlevel view of the collection process, read the collect
+  function.
+
+  TODO:
+	use a different interface for set_debug() (keywords)?
+	tune parameters
+
+*/
+
+
+#include "Python.h"
+
+#ifdef WITH_CYCLE_GC
+
+/* magic gc_refs value */
+#define GC_MOVED -1
+
+/*** Global GC state ***/
+
+/* linked lists of container objects */
+static PyGC_Head generation0 = {&generation0, &generation0, 0};
+static PyGC_Head generation1 = {&generation1, &generation1, 0};
+static PyGC_Head generation2 = {&generation2, &generation2, 0};
+static int generation = 0; /* current generation being collected */
+
+/* collection frequencies, XXX tune these */
+static int threshold0 = 100; /* net new containers before collection */
+static int threshold1 = 10;  /* generation0 collections before collecting 1 */
+static int threshold2 = 10;  /* generation1 collections before collecting 2 */
+
+/* net new objects allocated since last collection */
+static int allocated;
+
+/* set for debugging information */
+#define DEBUG_STATS		(1<<0) /* print collection statistics */
+#define DEBUG_COLLECTABLE	(1<<1) /* print collectable objects */
+#define DEBUG_UNCOLLECTABLE	(1<<2) /* print uncollectable objects */
+#define DEBUG_INSTANCES		(1<<3) /* print instances */
+#define DEBUG_OBJECTS		(1<<4) /* print other objects */
+#define DEBUG_LEAK		DEBUG_COLLECTABLE | \
+				DEBUG_UNCOLLECTABLE | \
+				DEBUG_INSTANCES | \
+				DEBUG_OBJECTS
+static int debug;
+
+/* list of uncollectable objects */
+static PyObject *garbage;
+
+
+/*** list functions ***/
+
+static void
+gc_list_init(PyGC_Head *list)
+{
+	list->gc_prev = list;
+	list->gc_next = list;
+}
+
+static void
+gc_list_append(PyGC_Head *node, PyGC_Head *list)
+{
+	node->gc_next = list;
+	node->gc_prev = list->gc_prev;
+	node->gc_prev->gc_next = node;
+	list->gc_prev = node;
+}
+
+static void
+gc_list_remove(PyGC_Head *node)
+{
+	node->gc_prev->gc_next = node->gc_next;
+	node->gc_next->gc_prev = node->gc_prev;
+#ifdef Py_DEBUG
+	node->gc_prev = NULL;
+	node->gc_next = NULL;
+#endif
+}
+
+static void 
+gc_list_move(PyGC_Head *from, PyGC_Head *to)
+{
+	if (from->gc_next == from) {
+		/* empty from list */
+		gc_list_init(to);
+	} else {
+		to->gc_next = from->gc_next;
+		to->gc_next->gc_prev = to;
+		to->gc_prev = from->gc_prev;
+		to->gc_prev->gc_next = to;
+	}
+	gc_list_init(from);
+}
+
+/* append a list onto another list, from becomes an empty list */
+static void
+gc_list_merge(PyGC_Head *from, PyGC_Head *to)
+{
+	PyGC_Head *tail;
+	if (from->gc_next != from) {
+		tail = to->gc_prev;
+		tail->gc_next = from->gc_next;
+		tail->gc_next->gc_prev = tail;
+		to->gc_prev = from->gc_prev;
+		to->gc_prev->gc_next = to;
+	}
+	gc_list_init(from);
+}
+
+static long
+gc_list_size(PyGC_Head *list)
+{
+	PyGC_Head *gc;
+	long n = 0;
+	for (gc = list->gc_next; gc != list; gc = gc->gc_next) {
+		n++;
+	}
+	return n;
+}
+
+/*** end of list stuff ***/
+
+
+/* Set all gc_refs = ob_refcnt */
+static void
+update_refs(PyGC_Head *containers)
+{
+	PyGC_Head *gc = containers->gc_next;
+	for (; gc != containers; gc=gc->gc_next) {
+		gc->gc_refs = PyObject_FROM_GC(gc)->ob_refcnt;
+	}
+}
+
+static int
+visit_decref(PyObject *op, void *data)
+{
+	if (op && PyObject_IS_GC(op)) {
+		PyObject_AS_GC(op)->gc_refs--;
+	}
+	return 0;
+}
+
+/* Subtract internal references from gc_refs */
+static void
+subtract_refs(PyGC_Head *containers)
+{
+	traverseproc traverse;
+	PyGC_Head *gc = containers->gc_next;
+	for (; gc != containers; gc=gc->gc_next) {
+		traverse = PyObject_FROM_GC(gc)->ob_type->tp_traverse;
+		(void) traverse(PyObject_FROM_GC(gc),
+			       (visitproc)visit_decref,
+			       NULL);
+	}
+}
+
+/* Append objects with gc_refs > 0 to roots list */
+static void
+move_roots(PyGC_Head *containers, PyGC_Head *roots)
+{
+	PyGC_Head *next;
+	PyGC_Head *gc = containers->gc_next;
+	while (gc != containers) {
+		next = gc->gc_next;
+		if (gc->gc_refs > 0) {
+			gc_list_remove(gc);
+			gc_list_append(gc, roots);
+			gc->gc_refs = GC_MOVED;
+		}
+		gc = next;
+	}
+}
+
+static int
+visit_reachable(PyObject *op, PyGC_Head *roots)
+{
+	if (PyObject_IS_GC(op)) {
+		PyGC_Head *gc = PyObject_AS_GC(op);
+		if (gc && gc->gc_refs != GC_MOVED) {
+			gc_list_remove(gc);
+			gc_list_append(gc, roots);
+			gc->gc_refs = GC_MOVED;
+		}
+	}
+	return 0;
+}
+
+/* Move objects referenced from reachable to reachable set. */
+static void
+move_root_reachable(PyGC_Head *reachable)
+{
+	traverseproc traverse;
+	PyGC_Head *gc = reachable->gc_next;
+	for (; gc != reachable; gc=gc->gc_next) {
+		/* careful, reachable list is growing here */
+		PyObject *op = PyObject_FROM_GC(gc);
+		traverse = op->ob_type->tp_traverse;
+		(void) traverse(op,
+			       (visitproc)visit_reachable,
+			       (void *)reachable);
+	}
+}
+
+/* move all objects with finalizers (instances with __del__) */
+static void
+move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
+{
+	PyGC_Head *next;
+	PyGC_Head *gc = unreachable->gc_next;
+	static PyObject *delstr;
+	if (delstr == NULL) {
+		delstr = PyString_InternFromString("__del__");
+	}
+	for (; gc != unreachable; gc=next) {
+		PyObject *op = PyObject_FROM_GC(gc);
+		next = gc->gc_next;
+		if (PyInstance_Check(op) && PyObject_HasAttr(op, delstr)) {
+			gc_list_remove(gc);
+			gc_list_append(gc, finalizers);
+		}
+	}
+}
+
+
+/* called by tp_traverse */
+static int
+visit_finalizer_reachable(PyObject *op, PyGC_Head *finalizers)
+{
+	if (PyObject_IS_GC(op)) {
+		PyGC_Head *gc = PyObject_AS_GC(op);
+		if (gc && gc->gc_refs != GC_MOVED) {
+			gc_list_remove(gc);
+			gc_list_append(gc, finalizers);
+			gc->gc_refs = GC_MOVED;
+		}
+	}
+	return 0;
+}
+
+/* Move objects referenced from roots to roots */
+static void
+move_finalizer_reachable(PyGC_Head *finalizers)
+{
+	traverseproc traverse;
+	PyGC_Head *gc = finalizers->gc_next;
+	for (; gc != finalizers; gc=gc->gc_next) {
+		/* careful, finalizers list is growing here */
+		traverse = PyObject_FROM_GC(gc)->ob_type->tp_traverse;
+		(void) traverse(PyObject_FROM_GC(gc), 
+			       (visitproc)visit_finalizer_reachable,
+			       (void *)finalizers);
+	}
+}
+
+static void
+debug_instance(PyObject *output, char *msg, PyInstanceObject *inst)
+{
+	char buf[200];
+	char *cname;
+	/* be careful not to create new dictionaries */
+	PyObject *classname = inst->in_class->cl_name;
+	if (classname != NULL && PyString_Check(classname))
+		cname = PyString_AsString(classname);
+	else
+		cname = "?";
+	sprintf(buf, "gc: %s<%.100s instance at %lx>\n", 
+			msg, cname, (long)inst);
+	PyFile_WriteString(buf, output);
+}
+
+static void
+debug_cycle(PyObject *output, char *msg, PyObject *op)
+{
+	if ((debug & DEBUG_INSTANCES) && PyInstance_Check(op)) {
+		debug_instance(output, msg, (PyInstanceObject *)op);
+	} else if (debug & DEBUG_OBJECTS) {
+		char buf[200];
+		sprintf(buf, "gc: %s<%s 0x%x>\n",
+				msg,
+				op->ob_type->tp_name,
+				(long)op);
+		PyFile_WriteString(buf, output);
+	}
+}
+
+/* Handle uncollectable garbage (cycles with finalizers). */
+static void
+handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
+{
+	PyGC_Head *gc;
+	if (garbage == NULL) {
+		garbage = PyList_New(0);
+	}
+	for (gc = finalizers->gc_next; gc != finalizers;
+			gc = finalizers->gc_next) {
+		PyObject *op = PyObject_FROM_GC(gc);
+		/* Add all instances to a Python accessible list of garbage */
+		if (PyInstance_Check(op)) {
+			PyList_Append(garbage, op);
+		}
+		/* We assume that all objects in finalizers are reachable from
+		 * instances.  Once we add the instances to the garbage list
+		 * everything is reachable from Python again. */
+		gc_list_remove(gc);
+		gc_list_append(gc, old);
+	}
+}
+
+/* Break reference cycles by clearing the containers involved.  This is
+ * tricky business as the lists can be changing and we don't know which
+ * objects may be freed.  It is possible I screwed something up here. */
+static void
+delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
+{
+	inquiry clear;
+
+	while (unreachable->gc_next != unreachable) {
+		PyGC_Head *gc = unreachable->gc_next;
+		PyObject *op = PyObject_FROM_GC(gc);
+		/*
+		PyList_Append(garbage, op);
+		*/
+		if ((clear = op->ob_type->tp_clear) != NULL) {
+			Py_INCREF(op);
+			clear((PyObject *)op);
+			Py_DECREF(op);
+		}
+		/* only try to call tp_clear once for each object */
+		if (unreachable->gc_next == gc) {
+			/* still alive, move it, it may die later */
+			gc_list_remove(gc);
+			gc_list_append(gc, old);
+		}
+	}
+}
+
+/* This is the main function.  Read this to understand how the
+ * collection process works. */
+static long
+collect(PyGC_Head *young, PyGC_Head *old)
+{
+	long n = 0;
+	long m = 0;
+	PyGC_Head reachable;
+	PyGC_Head unreachable;
+	PyGC_Head finalizers;
+	PyGC_Head *gc;
+	PyObject *output = NULL;
+
+	if (debug) {
+		output = PySys_GetObject("stderr");
+	}
+	if (debug & DEBUG_STATS) {
+		char buf[100];
+		sprintf(buf, "gc: collecting generation %d...\n", generation);
+		PyFile_WriteString(buf,output);
+		sprintf(buf, "gc: objects in each generation: %d %d %d\n",
+			gc_list_size(&generation0),
+			gc_list_size(&generation1),
+			gc_list_size(&generation2));
+		PyFile_WriteString(buf,output);
+	}
+
+	/* Using ob_refcnt and gc_refs, calculate which objects in the
+	 * container set are reachable from outside the set (ie. have a
+	 * refcount greater than 0 when all the references within the
+	 * set are taken into account */
+	update_refs(young);
+	subtract_refs(young);
+
+	/* Move everything reachable from outside the set into the
+	 * reachable set (ie. gc_refs > 0).  Next, move everything
+	 * reachable from objects in the reachable set. */
+	gc_list_init(&reachable);
+	move_roots(young, &reachable);
+	move_root_reachable(&reachable);
+
+	/* move unreachable objects to a temporary list, new objects can be
+	 * allocated after this point */
+	gc_list_init(&unreachable);
+	gc_list_move(young, &unreachable);
+
+	/* move reachable objects to next generation */
+	gc_list_merge(&reachable, old);
+
+	/* Move objects reachable from finalizers, we can't safely delete
+	 * them.  Python programmers should take care not to create such
+	 * things.  For Python finalizers means instance objects with
+	 * __del__ methods. */
+	gc_list_init(&finalizers);
+	move_finalizers(&unreachable, &finalizers);
+	move_finalizer_reachable(&finalizers);
+
+	/* Collect statistics on collectable objects found and print
+	 * debugging information. */
+	for (gc = unreachable.gc_next; gc != &unreachable;
+			gc = gc->gc_next) {
+		m++;
+		if (output != NULL && (debug & DEBUG_COLLECTABLE)) {
+			debug_cycle(output, "collectable ", PyObject_FROM_GC(gc));
+		}
+	}
+	/* call tp_clear on objects in the collectable set.  This will cause
+	 * the reference cycles to be broken. It may also cause some objects in
+	 * finalizers to be freed */
+	delete_garbage(&unreachable, old);
+
+	/* Collect statistics on uncollectable objects found and print
+	 * debugging information. */
+	for (gc = finalizers.gc_next; gc != &finalizers;
+			gc = gc->gc_next) {
+		n++;
+		if (output != NULL && (debug & DEBUG_UNCOLLECTABLE)) {
+			debug_cycle(output, "uncollectable ", PyObject_FROM_GC(gc));
+		}
+	}
+	if (output != NULL && (debug & DEBUG_STATS)) {
+		if (m == 0 && n == 0) {
+			PyFile_WriteString("gc: done.\n", output);
+		} else {
+			char buf[200];
+			sprintf(buf,
+				"gc: done, %d unreachable, %d uncollectable.\n",
+				n+m, n);
+			PyFile_WriteString(buf, output);
+		}
+	}
+
+	/* Append instances in the uncollectable set to a Python
+	 * reachable list of garbage.  The programmer has to deal with
+	 * this if they insist on creating this type of structure. */
+	handle_finalizers(&finalizers, old);
+
+	allocated = 0;
+	PyErr_Clear(); /* in case writing to sys.stderr failed */
+	return n+m;
+}
+
+static long
+collect_generations(void)
+{
+	static long collections0 = 0;
+	static long collections1 = 0;
+	long n;
+
+
+	if (collections1 > threshold2) {
+		generation = 2;
+		gc_list_merge(&generation0, &generation2);
+		gc_list_merge(&generation1, &generation2);
+		if (generation2.gc_next != &generation2) {
+			n = collect(&generation2, &generation2);
+		}
+		collections1 = 0;
+	} else if (collections0 > threshold1) {
+		generation = 1;
+		collections1++;
+		gc_list_merge(&generation0, &generation1);
+		if (generation1.gc_next != &generation1) {
+			n = collect(&generation1, &generation2);
+		}
+		collections0 = 0;
+	} else {
+		generation = 0;
+		collections0++;
+		if (generation0.gc_next != &generation0) {
+			n = collect(&generation0, &generation1);
+		}
+	}
+	return n;
+}
+
+void
+_PyGC_Insert(PyObject *op)
+{
+	/* collection lock since collecting may cause allocations */
+	static int collecting = 0;
+
+#ifdef Py_DEBUG
+	if (!PyObject_IS_GC(op)) {
+		abort();
+	}
+#endif
+	if (threshold0 && allocated > threshold0 && !collecting) {
+		collecting++;
+		collect_generations();
+		collecting--;
+	}
+	allocated++;
+	gc_list_append(PyObject_AS_GC(op), &generation0);
+}
+
+void
+_PyGC_Remove(PyObject *op)
+{
+	PyGC_Head *g = PyObject_AS_GC(op);
+#ifdef Py_DEBUG
+	if (!PyObject_IS_GC(op)) {
+		abort();
+	}
+#endif
+	gc_list_remove(g);
+	if (allocated > 0) {
+		allocated--;
+	}
+}
+
+
+static char collect__doc__[] =
+"collect() -> n\n"
+"\n"
+"Run a full collection.  The number of unreachable objects is returned.\n"
+;
+
+static PyObject *
+Py_collect(self, args)
+	PyObject *self;
+	PyObject *args;
+{
+	long n;
+
+	if(!PyArg_ParseTuple(args, ""))	/* check no args */
+		return NULL;
+
+	generation = 2;
+	gc_list_merge(&generation0, &generation2);
+	gc_list_merge(&generation1, &generation2);
+	n = collect(&generation2, &generation2);
+
+	return Py_BuildValue("i", n);
+}
+
+static char set_debug__doc__[] = 
+"set_debug(flags) -> None\n"
+"\n"
+"Set the garbage collection debugging flags. Debugging information is\n"
+"written to sys.stderr.\n"
+"\n"
+"flags is an integer and can have the following bits turned on:\n"
+"\n"
+"  DEBUG_STATS - Print statistics during collection.\n"
+"  DEBUG_COLLECTABLE - Print collectable objects found.\n"
+"  DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n"
+"  DEBUG_INSTANCES - Print instance objects.\n"
+"  DEBUG_OBJECTS - Print objects other than instances.\n"
+"  DEBUG_LEAK - Debug leaking programs (everything but STATS).\n"
+;
+
+static PyObject *
+Py_set_debug(self, args)
+	PyObject *self;
+	PyObject *args;
+{
+	if (!PyArg_ParseTuple(args, "l", &debug))
+		return NULL;
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static char get_debug__doc__[] = 
+"get_debug() -> flags\n"
+"\n"
+"Get the garbage collection debugging flags.\n"
+;
+
+static PyObject *
+Py_get_debug(self, args)
+	PyObject *self;
+	PyObject *args;
+{
+	if(!PyArg_ParseTuple(args, ""))	/* no args */
+		return NULL;
+
+	return Py_BuildValue("i", debug);
+}
+
+static char set_thresh__doc__[] =
+"set_threshold(threshold0, [threhold1, threshold2]) -> None\n"
+"\n"
+"Sets the collection thresholds.  Setting threshold0 to zero disables\n"
+"collection.\n"
+;
+
+static PyObject *
+Py_set_thresh(self, args)
+	PyObject *self;
+	PyObject *args;
+{
+	if (!PyArg_ParseTuple(args, "i|ii", &threshold0, 
+				&threshold1, &threshold2))
+		return NULL;
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static char get_thresh__doc__[] =
+"get_threshold() -> (threshold0, threshold1, threshold2)\n"
+"\n"
+"Return the current collection thresholds\n"
+;
+
+static PyObject *
+Py_get_thresh(self, args)
+	PyObject *self;
+	PyObject *args;
+{
+	if(!PyArg_ParseTuple(args, ""))	/* no args */
+		return NULL;
+
+	return Py_BuildValue("(iii)", threshold0, threshold1, threshold2);
+}
+
+
+static char gc__doc__ [] =
+"This module provides access to the garbage collector for reference cycles.\n"
+"\n"
+"collect() -- Do a full collection right now.\n"
+"set_debug() -- Set debugging flags.\n"
+"get_debug() -- Get debugging flags.\n"
+"set_threshold() -- Set the collection thresholds.\n"
+"get_threshold() -- Return the current the collection thresholds.\n"
+;
+
+static PyMethodDef GcMethods[] = {
+	{"set_debug",		Py_set_debug,  METH_VARARGS, set_debug__doc__},
+	{"get_debug",		Py_get_debug,  METH_VARARGS, get_debug__doc__},
+	{"set_threshold",	Py_set_thresh, METH_VARARGS, set_thresh__doc__},
+	{"get_threshold",	Py_get_thresh, METH_VARARGS, get_thresh__doc__},
+	{"collect",		Py_collect,    METH_VARARGS, collect__doc__},
+	{NULL,	NULL}		/* Sentinel */
+};
+
+void
+initgc(void)
+{
+	PyObject *m;
+	PyObject *d;
+
+	m = Py_InitModule4("gc",
+			      GcMethods,
+			      gc__doc__,
+			      NULL,
+			      PYTHON_API_VERSION);
+	d = PyModule_GetDict(m);
+	if (garbage == NULL) {
+		garbage = PyList_New(0);
+	}
+	PyDict_SetItemString(d, "garbage", garbage);
+	PyDict_SetItemString(d, "DEBUG_STATS",
+			PyInt_FromLong(DEBUG_STATS));
+	PyDict_SetItemString(d, "DEBUG_COLLECTABLE",
+			PyInt_FromLong(DEBUG_COLLECTABLE));
+	PyDict_SetItemString(d, "DEBUG_UNCOLLECTABLE",
+			PyInt_FromLong(DEBUG_UNCOLLECTABLE));
+	PyDict_SetItemString(d, "DEBUG_INSTANCES",
+			PyInt_FromLong(DEBUG_INSTANCES));
+	PyDict_SetItemString(d, "DEBUG_OBJECTS",
+			PyInt_FromLong(DEBUG_OBJECTS));
+	PyDict_SetItemString(d, "DEBUG_LEAK",
+			PyInt_FromLong(DEBUG_LEAK));
+}
+
+#endif /* WITH_CYCLE_GC */
diff --git a/Modules/newmodule.c b/Modules/newmodule.c
index fa0dc8b..d35896c 100644
--- a/Modules/newmodule.c
+++ b/Modules/newmodule.c
@@ -56,6 +56,7 @@
 	Py_INCREF(dict);
 	inst->in_class = (PyClassObject *)klass;
 	inst->in_dict = dict;
+	PyObject_GC_Init(inst);
 	return (PyObject *)inst;
 }
 
diff --git a/Objects/classobject.c b/Objects/classobject.c
index f1dabfe..4653d27 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -132,6 +132,7 @@
 	Py_XINCREF(op->cl_getattr);
 	Py_XINCREF(op->cl_setattr);
 	Py_XINCREF(op->cl_delattr);
+	PyObject_GC_Init(op);
 	return (PyObject *) op;
 }
 
@@ -141,6 +142,7 @@
 class_dealloc(op)
 	PyClassObject *op;
 {
+	PyObject_GC_Fini(op);
 	Py_DECREF(op->cl_bases);
 	Py_DECREF(op->cl_dict);
 	Py_XDECREF(op->cl_name);
@@ -428,7 +430,7 @@
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"class",
-	sizeof(PyClassObject) + PyGC_INFO_SIZE,
+	sizeof(PyClassObject) + PyGC_HEAD_SIZE,
 	0,
 	(destructor)class_dealloc, /*tp_dealloc*/
 	0,		/*tp_print*/
@@ -490,6 +492,7 @@
 	if (inst == NULL)
 		return NULL;
 	inst->in_dict = PyDict_New();
+	PyObject_GC_Init(inst);
 	if (inst->in_dict == NULL) {
 		PyObject_DEL(inst);
 		return NULL;
@@ -539,11 +542,12 @@
 	PyObject *error_type, *error_value, *error_traceback;
 	PyObject *del;
 	static PyObject *delstr;
+	extern long _Py_RefTotal;
+	PyObject_GC_Fini(inst);
 	/* Call the __del__ method if it exists.  First temporarily
 	   revive the object and save the current exception, if any. */
 #ifdef Py_TRACE_REFS
 	/* much too complicated if Py_TRACE_REFS defined */
-	extern long _Py_RefTotal;
 	inst->ob_type = &PyInstance_Type;
 	_Py_NewReference((PyObject *)inst);
 	_Py_RefTotal--;		/* compensate for increment in NEWREF */
@@ -591,6 +595,7 @@
 #ifdef COUNT_ALLOCS
 		inst->ob_type->tp_free--;
 #endif
+		PyObject_GC_Init((PyObject *)inst);
 		return; /* __del__ added a reference; don't delete now */
 	}
 #ifdef Py_TRACE_REFS
@@ -598,7 +603,9 @@
 	inst->ob_type->tp_free--;	/* compensate for increment in UNREF */
 #endif
 	_Py_ForgetReference((PyObject *)inst);
+#ifndef WITH_CYCLE_GC
 	inst->ob_type = NULL;
+#endif
 #endif /* Py_TRACE_REFS */
 	Py_DECREF(inst->in_class);
 	Py_XDECREF(inst->in_dict);
@@ -1510,7 +1517,7 @@
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"instance",
-	sizeof(PyInstanceObject) + PyGC_INFO_SIZE,
+	sizeof(PyInstanceObject) + PyGC_HEAD_SIZE,
 	0,
 	(destructor)instance_dealloc, /*tp_dealloc*/
 	0,			/*tp_print*/
@@ -1568,6 +1575,7 @@
 	im->im_self = self;
 	Py_INCREF(class);
 	im->im_class = class;
+	PyObject_GC_Init(im);
 	return (PyObject *)im;
 }
 
@@ -1643,6 +1651,7 @@
 instancemethod_dealloc(im)
 	register PyMethodObject *im;
 {
+	PyObject_GC_Fini(im);
 	Py_DECREF(im->im_func);
 	Py_XDECREF(im->im_self);
 	Py_DECREF(im->im_class);
@@ -1745,7 +1754,7 @@
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"instance method",
-	sizeof(PyMethodObject) + PyGC_INFO_SIZE,
+	sizeof(PyMethodObject) + PyGC_HEAD_SIZE,
 	0,
 	(destructor)instancemethod_dealloc, /*tp_dealloc*/
 	0,			/*tp_print*/
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index ce38f11..ea3af48 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -129,6 +129,7 @@
 	mp->ma_table = NULL;
 	mp->ma_fill = 0;
 	mp->ma_used = 0;
+	PyObject_GC_Init(mp);
 	return (PyObject *)mp;
 }
 
@@ -481,6 +482,7 @@
 	register int i;
 	register dictentry *ep;
 	Py_TRASHCAN_SAFE_BEGIN(mp)
+	PyObject_GC_Fini(mp);
 	for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
 		if (ep->me_key != NULL) {
 			Py_DECREF(ep->me_key);
@@ -1087,7 +1089,7 @@
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"dictionary",
-	sizeof(dictobject) + PyGC_INFO_SIZE,
+	sizeof(dictobject) + PyGC_HEAD_SIZE,
 	0,
 	(destructor)dict_dealloc, /*tp_dealloc*/
 	(printfunc)dict_print, /*tp_print*/
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index d9ce027..2736d65 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -63,6 +63,7 @@
 		Py_INCREF(doc);
 		op->func_doc = doc;
 	}
+	PyObject_GC_Init(op);
 	return (PyObject *)op;
 }
 
@@ -186,6 +187,7 @@
 func_dealloc(op)
 	PyFunctionObject *op;
 {
+	PyObject_GC_Fini(op);
 	Py_DECREF(op->func_code);
 	Py_DECREF(op->func_globals);
 	Py_DECREF(op->func_name);
@@ -277,7 +279,7 @@
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"function",
-	sizeof(PyFunctionObject) + PyGC_INFO_SIZE,
+	sizeof(PyFunctionObject) + PyGC_HEAD_SIZE,
 	0,
 	(destructor)func_dealloc, /*tp_dealloc*/
 	0,		/*tp_print*/
diff --git a/Objects/listobject.c b/Objects/listobject.c
index e9f12ab..a75100e 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -72,10 +72,11 @@
 	}
 	/* PyObject_NewVar is inlined */
 	op = (PyListObject *) PyObject_MALLOC(sizeof(PyListObject)
-						+ PyGC_INFO_SIZE);
+						+ PyGC_HEAD_SIZE);
 	if (op == NULL) {
 		return PyErr_NoMemory();
 	}
+	op = (PyListObject *) PyObject_FROM_GC(op);
 	if (size <= 0) {
 		op->ob_item = NULL;
 	}
@@ -89,6 +90,7 @@
 	PyObject_INIT_VAR(op, &PyList_Type, size);
 	for (i = 0; i < size; i++)
 		op->ob_item[i] = NULL;
+	PyObject_GC_Init(op);
 	return (PyObject *) op;
 }
 
@@ -216,6 +218,7 @@
 {
 	int i;
 	Py_TRASHCAN_SAFE_BEGIN(op)
+	PyObject_GC_Fini(op);
 	if (op->ob_item != NULL) {
 		/* Do it backwards, for Christian Tismer.
 		   There's a simple test case where somehow this reduces
@@ -1498,7 +1501,7 @@
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"list",
-	sizeof(PyListObject) + PyGC_INFO_SIZE,
+	sizeof(PyListObject) + PyGC_HEAD_SIZE,
 	0,
 	(destructor)list_dealloc, /*tp_dealloc*/
 	(printfunc)list_print, /*tp_print*/
@@ -1577,7 +1580,7 @@
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"list (immutable, during sort)",
-	sizeof(PyListObject) + PyGC_INFO_SIZE,
+	sizeof(PyListObject) + PyGC_HEAD_SIZE,
 	0,
 	0,		/*tp_dealloc*/ /* Cannot happen */
 	(printfunc)list_print, /*tp_print*/
diff --git a/Objects/object.c b/Objects/object.c
index 9ed03b2..6eaff67 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -124,6 +124,10 @@
 				"NULL object passed to PyObject_Init");
 		return op;
   	}
+#ifdef WITH_CYCLE_GC
+	if (PyType_IS_GC(tp))
+		op = (PyObject *) PyObject_FROM_GC(op);
+#endif
 	/* Any changes should be reflected in PyObject_INIT (objimpl.h) */
 	op->ob_type = tp;
 	_Py_NewReference(op);
@@ -141,6 +145,10 @@
 				"NULL object passed to PyObject_InitVar");
 		return op;
 	}
+#ifdef WITH_CYCLE_GC
+	if (PyType_IS_GC(tp))
+		op = (PyVarObject *) PyObject_FROM_GC(op);
+#endif
 	/* Any changes should be reflected in PyObject_INIT_VAR */
 	op->ob_size = size;
 	op->ob_type = tp;
@@ -156,6 +164,10 @@
 	op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp));
 	if (op == NULL)
 		return PyErr_NoMemory();
+#ifdef WITH_CYCLE_GC
+	if (PyType_IS_GC(tp))
+		op = (PyObject *) PyObject_FROM_GC(op);
+#endif
 	return PyObject_INIT(op, tp);
 }
 
@@ -168,6 +180,10 @@
 	op = (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size));
 	if (op == NULL)
 		return (PyVarObject *)PyErr_NoMemory();
+#ifdef WITH_CYCLE_GC
+	if (PyType_IS_GC(tp))
+		op = (PyVarObject *) PyObject_FROM_GC(op);
+#endif
 	return PyObject_INIT_VAR(op, tp, size);
 }
 
@@ -175,9 +191,23 @@
 _PyObject_Del(op)
 	PyObject *op;
 {
-	PyObject_FREE(op);
+#ifdef WITH_CYCLE_GC
+	if (PyType_IS_GC(op->ob_type)) {
+		PyGC_Head *g = PyObject_AS_GC(op);
+		PyObject_FREE(g);
+	} else
+#endif
+	{
+		PyObject_FREE(op);
+	}
 }
 
+#ifndef WITH_CYCLE_GC
+/* extension modules might need these */
+void _PyGC_Insert(PyObject *op) { }
+void _PyGC_Remove(PyObject *op) { }
+#endif
+
 int
 PyObject_Print(op, fp, flags)
 	PyObject *op;
@@ -917,8 +947,10 @@
 {
 	destructor dealloc = op->ob_type->tp_dealloc;
 	_Py_ForgetReference(op);
+#ifndef WITH_CYCLE_GC
 	if (_PyTrash_delete_nesting < PyTrash_UNWIND_LEVEL-1)
 		op->ob_type = NULL;
+#endif
 	(*dealloc)(op);
 }
 
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 73e3304..46e67a8 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -94,7 +94,7 @@
 		/* Check for overflow */
 		if (nbytes / sizeof(PyObject *) != (size_t)size ||
 		    (nbytes += sizeof(PyTupleObject) - sizeof(PyObject *)
-		     		+ PyGC_INFO_SIZE)
+		     		+ PyGC_HEAD_SIZE)
 		    <= 0)
 		{
 			return PyErr_NoMemory();
@@ -103,7 +103,7 @@
 		op = (PyTupleObject *) PyObject_MALLOC(nbytes);
 		if (op == NULL)
 			return PyErr_NoMemory();
-
+		op = (PyTupleObject *) PyObject_FROM_GC(op);
 		PyObject_INIT_VAR(op, &PyTuple_Type, size);
 	}
 	for (i = 0; i < size; i++)
@@ -115,6 +115,7 @@
 		Py_INCREF(op);	/* extra INCREF so that this is never freed */
 	}
 #endif
+	PyObject_GC_Init(op);
 	return (PyObject *) op;
 }
 
@@ -181,6 +182,7 @@
 	register int i;
 	register int len =  op->ob_size;
 	Py_TRASHCAN_SAFE_BEGIN(op)
+	PyObject_GC_Fini(op);
 	if (len > 0) {
 		i = len;
 		while (--i >= 0)
@@ -453,7 +455,7 @@
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
 	"tuple",
-	sizeof(PyTupleObject) - sizeof(PyObject *) + PyGC_INFO_SIZE,
+	sizeof(PyTupleObject) - sizeof(PyObject *) + PyGC_HEAD_SIZE,
 	sizeof(PyObject *),
 	(destructor)tupledealloc, /*tp_dealloc*/
 	(printfunc)tupleprint, /*tp_print*/
@@ -557,12 +559,27 @@
 	} else 
 #endif		
 	{
+#ifdef WITH_CYCLE_GC
+		PyGC_Head *g = PyObject_AS_GC((PyObject *)v);
+		PyObject_GC_Fini((PyObject *)v);
+		sv = (PyTupleObject *)
+			PyObject_REALLOC((char *)g, sizeof(PyTupleObject) 
+					+ PyGC_HEAD_SIZE
+					+ newsize * sizeof(PyObject *));
+		if (g == NULL) {
+			sv = NULL;
+		} else {
+			sv = (PyTupleObject *)PyObject_FROM_GC(g);
+		}
+#else
 		sv = (PyTupleObject *)
 			PyObject_REALLOC((char *)v, sizeof(PyTupleObject) 
-					+ PyGC_INFO_SIZE
+					+ PyGC_HEAD_SIZE
 					+ newsize * sizeof(PyObject *));
+#endif
 		*pv = (PyObject *) sv;
 		if (sv == NULL) {
+			PyObject_GC_Init((PyObject *)v);
 			PyObject_DEL(v);
 			PyErr_NoMemory();
 			return -1;
@@ -578,6 +595,7 @@
 			sv->ob_item[i - sizediff] = NULL;
 		}
 	}
+	PyObject_GC_Init(sv);
 	sv->ob_size = newsize;
 	return 0;
 }
diff --git a/PC/config.c b/PC/config.c
index f295f85..0ff4c93 100644
--- a/PC/config.c
+++ b/PC/config.c
@@ -43,6 +43,9 @@
 #endif
 extern void initcmath();
 extern void initerrno();
+#ifdef WITH_CYCLE_GC
+extern void initgc();
+#endif
 #ifndef MS_WIN64
 extern void initimageop();
 #endif
@@ -89,6 +92,9 @@
 #endif
         {"cmath", initcmath},
         {"errno", initerrno},
+#ifdef WITH_CYCLE_GC
+        {"gc", initgc},
+#endif
 #ifndef MS_WIN64
         {"imageop", initimageop},
 #endif
diff --git a/PC/config.h b/PC/config.h
index dfe5118..43bb35f 100644
--- a/PC/config.h
+++ b/PC/config.h
@@ -451,6 +451,9 @@
 /* Define if you want to use the GNU readline library */
 /* #define WITH_READLINE 1 */
 
+/* Define if you want cycle garbage collection */
+/* #define WITH_CYCLE_GC 1 */
+
 /* Define if you have clock.  */
 /* #define HAVE_CLOCK */
 
diff --git a/PCbuild/python16.dsp b/PCbuild/python16.dsp
index 4c75302..34ec9ef 100644
--- a/PCbuild/python16.dsp
+++ b/PCbuild/python16.dsp
@@ -659,6 +659,20 @@
 

 # End Source File

 # Begin Source File

+SOURCE=..\Modules\gcmodule.c

+

+!IF  "$(CFG)" == "python16 - Win32 Release"

+

+!ELSEIF  "$(CFG)" == "python16 - Win32 Debug"

+

+!ELSEIF  "$(CFG)" == "python16 - Win32 Alpha Debug"

+

+!ELSEIF  "$(CFG)" == "python16 - Win32 Alpha Release"

+

+!ENDIF 

+

+# End Source File
+# Begin Source File

 

 SOURCE=..\Python\getargs.c