[Python-Dev] __traceback__ and reference cycles

Tim Peters tim.peters at gmail.com
Tue Aug 9 03:12:49 CEST 2005


[Armin Rigo]
> There are various proposals to add an attribute on exception instances
> to store the traceback (see PEP 344).  A detail not discussed, which I
> thought of historical interest only, is that today's exceptions try very
> hard to avoid reference cycles, in particular the cycle
>
>   'frame -> local variable -> traceback object -> frame'
>
> which was important for pre-GC versions of Python.  A clause 'except
> Exception, e' would not create a local reference to the traceback, only
> to the exception instance.  If the latter grows a __traceback__
> attribute, it is no longer true, and every such except clause typically
> creates a cycle.
>
> Of course, we don't care, we have a GC -- do we?  Well, there are cases
> where we do: see the attached program...  In my opinion it should be
> considered a bug of today's Python that this program leaks memory very
> fast and takes longer and longer to run each loop (each loop takes half
> a second longer than the previous one!).  (I don't know how this bug
> could be fixed, though.)
>
> Spoiling the fun of figuring out what is going on, the reason is that
> 'e_tb' creates a reference cycle involving the frame of __del__, which
> keeps a reference to 'self' alive.  Python thinks 'self' was
> resurrected.  The next time the GC runs, the cycle disappears, and the
> refcount of 'self' drops to zero again, calling __del__ again -- which
> gets resurrected again by a new cycle.  Etc...  Note that no cycle
> actually contains 'self'; they just point to 'self'.  In summary, no X
> instance gets ever freed, but they all have their destructors called
> over and over again.
>
> Attaching a __traceback__ will only make this "bug" show up more often,
> as the 'except Exception, e' line in a __del__() method would be enough
> to trigger it.
>
> Not sure what to do about it.  I just thought I should share these
> thoughts (I stumbled over almost this problem in PyPy).

I can't think of a Python feature with a higher aggregate
braincell_burned / benefit ratio than __del__ methods.  If P3K retains
them-- or maybe even before --we should consider taking "the Java
dodge" on this one.  That is, decree that henceforth a __del__ method
will get invoked by magic at most once on any given object O, no
matter how often O is resurrected.

It's been mentioned before, but it's at least theoretically
backward-incompatible, so "it's scary".  I can guarantee I don't have
any code that would care, including all the ZODB code I watch over
these days.  For ZODB it's especially easy to be sure of this:  the
only __del__ method in the whole thing appears in the test suite,
verifying that ZODB's object cache no longer gets into an infinite
loop when a user-defined persistent object has a brain-dead __del__
method that reloads self from the database.  (Interestingly enough, if
Python guaranteed to call __del__ at most once, the infinite loop in
ZODB's object cache never would have appeared in this case.)


More information about the Python-Dev mailing list