Most of the stuff on this page is obsolete nowadays, since Python (since version 2.0, I think) now includes a cyclical-reference garbage collector. Leaks like this are still possible, but usually only because an older Python extension module (implementing a container type) is used, one that doesn't adhere to the new GC API.
Long-running processes have a nasty habit of exposing Python's
Achilles' Heel: Memory Leaks created by cycles. (objects that
point at each other, either directly or circuitously). Reference counting
cannot collect cycles. Here's one way to create a cycle:
class thing:
pass
refcount(a) refcount(b)
a = thing() 1
b = thing() 1 1
a.other = b 1 2
b.other = a 2 2
del a 1 2
del b 1 1
Objects a
and b
have become immortal.
Large and complex systems may create non-obvious cycles. Here are a few quick hints to avoid various ones that I've run into:
del
this object manually, or change your
code.
del
a traceback if you have a handle to it.
Another good idea is to assign None
to both
sys.traceback
and sys.exc_traceback
.
def my_method (self): try: do_something() except: try: ei = sys.exc_info() [... report error ...] finally: del ei...otherwise it will capture references to every object in the socket map. I have plugged some really bad leaks this way.
class thing: all_things = {} def __init__ (self): thing.all_things[id(self)] = 1 def __del__ (self): del thing.all_things[id(self)]Here is a module that will let you resurrect leaked objects. Using this module should fill you with shame. Make sure no one is looking.
for addr in thing.all_things.keys(): r = resurrect.conjure (addr) # examine r...
# -*- Mode: Python; tab-width: 4 -*- import sys import types def get_refcounts(): d = {} sys.modules # collect all classes for m in sys.modules.values(): for sym in dir(m): o = getattr (m, sym) if type(o) is types.ClassType: d[o] = sys.getrefcount (o) # sort by refcount pairs = map (lambda x: (x[1],x[0]), d.items()) pairs.sort() pairs.reverse() return pairs def print_top_100(): for n, c in get_refcounts()[:100]: print '%10d %s' % (n, c.__name__) if __name__ == '__main__': top_100()