[Python-Dev] Reference cycles in Exception.__traceback__

Victor Stinner victor.stinner at gmail.com
Fri Mar 7 12:14:15 CET 2014


2014-03-07 6:25 GMT+01:00 Nick Coghlan <ncoghlan at gmail.com>:
>> Uh, really? If you want to suppress all reference cycles, you *have* to
>> remove __traceback__.
>>
>> The problem is to make computation of the traceback summary lightweight
>> enough that it doesn't degrade performance in the common case where you
>> don't have to print the traceback later.
>
> The proposed summary extraction only keeps the exception type and its str
> output, not the exception itself (as you don't need that to create the
> formatted traceback).

My patch keeps the exception object, but it breaks links to the other
exceptions (__cause__ and __context__) and to the traceback
(__traceback__):
https://bitbucket.org/haypo/misc/src/tip/python/suppress_locals.py

Then I saw that http://bugs.python.org/issue17911 already has a patch
for the traceback module, nice! And it's very close to what I wrote:
http://bugs.python.org/review/17911/#ps8639

This patch stores the exception as string
(traceback._format_value(exc)). I prefer to avoid any useless
formatting, since the formatted exception is only needed in rare
cases. Just drop the "exception summary/view" is the most common case.

If you want to full original exception object, don't use the
summary/view but handle the exception in the except block.


I realized that "memory leaks" (reference cycles) is a common issue
with traceback objects (in Python 2) and exception objects (in Python
3):

Extracting tracebacks does too much work [open]
http://bugs.python.org/issue17911

Local variables not freed when Exception raises in function called
from cycle [wont fix]
http://bugs.python.org/issue5641

asyncio.Future.set_exception() creates a reference cycle [invalid]
http://bugs.python.org/issue20032

Do we need to call gc.collect() occasionally through the event loop?
http://code.google.com/p/tulip/issues/detail?id=42

Traceback objects not properly garbage-collected [invalid]
http://bugs.python.org/issue226254

Reference cycle in _TracebackLogger and bug in _TracebackLogger.__del__()
http://code.google.com/p/tulip/issues/detail?id=155

Twisted fake Traceback object:
http://twistedmatrix.com/trac/browser/trunk/twisted/python/failure.py#L89

frame.f_locals keeps references to things for too long [open since 2009],
request from Twisted
http://bugs.python.org/issue6116
http://twistedmatrix.com/trac/ticket/3853

assertRaises as a context manager keeps tracebacks and frames alive [open]
http://bugs.python.org/issue9815

Expose called function on frame object
http://bugs.python.org/issue12857

tracebacks eat up memory by holding references to locals and globals
when they are not wanted [fixed by traceback.clear_frames()]
http://bugs.python.org/issue1565525

Add a frame method to clear expensive details [fixed by frame.clear()]
http://bugs.python.org/issue17934

Generator cleanup without tp_del [rejected]
http://bugs.python.org/issue17807

Generator memory leak [duplicate]
http://bugs.python.org/issue17468

asyncio: remove _TracebackLogger [fixed]
http://bugs.python.org/issue19967

sys.exc_info() should not be stored on a local variable [fixed]
https://code.djangoproject.com/ticket/10758

Capturing the Currently Raised Exception
http://docs.python.org/3/howto/pyporting.html#capturing-the-currently-raised-exception
    In Python 3, the traceback is attached to the exception instance through
    the __traceback__ attribute. If the instance is saved in a local variable
    that persists outside of the except block, the traceback will create a
    reference cycle with the current frame and its dictionary of local
    variables. This will delay reclaiming dead resources until the next cyclic
    garbage collection pass.

    In Python 2, this problem only occurs if you save the traceback itself
    (e.g.  the third element of the tuple returned by sys.exc_info()) in a
    variable.

    => http://hewgill.com/journal/entries/541-python-2-to-3-upgrade-and-exception-handling

[Python-Dev] new unbounded memory leak in exception handling?
https://mail.python.org/pipermail/python-dev/2009-November/094304.html

PEP 3134: Exception Chaining and Embedded Tracebacks [final]
http://legacy.python.org/dev/peps/pep-3134/

PEP 344: Exception Chaining and Embedded Tracebacks [superseded]
http://legacy.python.org/dev/peps/pep-0344/

Victor


More information about the Python-Dev mailing list