[Python-Dev] Cyclic GC issues

Tim Peters tim.peters at gmail.com
Mon Oct 11 01:18:51 CEST 2004

[Martin v. Löwis]
> Hmmm. Even if a ring member holds only a single reference to another
> ring member, would not calling visit() on this member in turn invoke
> visit() on the other one, which would, in turn, invoke visit() on
> the next one, and so on?

Yes, but not recursively (which that description appears to imply),
and not because of what's passed *to* visit().  More precisely,
visit(some_pointer) does different things during different phases of
gc (different phases pass different gcmodule.c callback functions to
tp_traverse() implementations).  None of them lead to recursive calls
to the callback.  Maybe a bit ironically, refcount-based deletion can
require unbounded stack depth (hence the "trashcan" mechanism to avoid
blowing the C stack), but Python's cyclic gc isn't recursive at all.

I'm guessing that Jason is confusing the update_refs() and
subtract_refs() phases of Python's gc with the "mark" phase of a
mark/sweep collector.  If so, the visit() callbacks used by those are
actually irrelevant to the set of objects whose tp_traverse() is
called during those phases.  The tp_traverse() of *every* object in
the generation being collected is invoked by that pair of phases, and
the set of objects passed *to* the visit callbacks during those phases
has no effect on the set of objects whose tp_traverse() gets called
during those phases.

So one of Jason's objects will get its tp_traverse() called by those
phases if and only if it's in the generation currently being
collected, and it's currently tracked by gc.  It doesn't matter
whether, e.g., anything points at it, or whether it points at anything
(although if nothing anywhere points at it, it would already have been
destroyed via refcounting).

More information about the Python-Dev mailing list