[Python-Dev] Bizarre new test failure

Tim Peters tim.one@comcast.net
Fri, 07 Jun 2002 13:51:16 -0400


[Jeremy Hylton, explains the cells in Neil's example]

Thanks!  That was helpful.

> ...
> class A refers to
> function __init__ refers to
> cell for A refers to
> class A
>
> That's it for what I understand.

Where does the singleton tuple containing a cell come from?  I guess it must
be in function __init__.

> It looks like the example code creates two cycles, and one cycle
> refers to the other.  The first cycle is the one involving the A
> instance and the super instance variable.  That cycle has a reference
> to class A.
>
> When the garbage collector runs, it determines the first cycle is
> garbage.  It doesn't determine the second cycle is garbage because it
> has an external reference from the first cycle.

The proper understanding of "external" here is wrt all the objects GC
tracks.  So long as references are *within* that grand set, there are no
external references in a relevant sense.  "External" means stuff like the
reference is due to an untracked container, or to a C variable -- stuff like
that.

> I presume that the garbage collector can't collect both cycles in one
> pass without re-running the update & subtract refs phase after
> deleting all the garbage.  During the first refs pass, the second
> cycle wasn't detected.  The second cycle is only collectable after the
> first cycle has been collected.

I don't think that's it.  Here:

C:\Code\python\PCbuild>python
Python 2.3a0 (#29, Jun  5 2002, 23:17:02) [MSC 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class A: pass
...
>>> class B: pass
...
>>> A.a = A # one cycle
>>> B.b = B # another
>>> A.b = B # point the first cycle at the second
>>> import gc
>>> gc.collect()
0
>>> gc.set_debug(gc.DEBUG_SAVEALL)
>>> del B
>>> gc.collect()   # A still keeping everything alive
0
>>> del A          # Both cycles are trash now
>>> gc.collect()   # And both are recoved in one pass
4
>>> print gc.garbage
[<class __main__.A at 0x0065D120>,

 {'a': <class __main__.A at 0x0065D120>,
  '__module__': '__main__',
  'b': <class __main__.B at 0x0065D1B0>,
  '__doc__': None
 },

 <class __main__.B at 0x0065D1B0>,

 {'__module__': '__main__',
  'b': <class __main__.B at 0x0065D1B0>,
  '__doc__': None
 }
]
>>>