Hunting a memory leak

Edward K. Ream edreamleo at charter.net
Fri Aug 29 10:00:00 EDT 2003


> I'm trying to discover a memory leak on a program of mine. I've taken
> several approaches, but the leak still resists to appear.

First, single-stepping through C code is surprisingly effective.  I heartily
recommend it.

Here are some ideas you might use if you are truly desperate.  You will have
to do some work to make them useful in your situation.

1.  Keep track of all newly-created objects.  Warning: the id trick used in
this code is not proper because newly allocated objects can have the same
address as old objects, so you should devise a better way by creating a more
unique hash.  Or just use the code as is and see whether the "invalid" code
tells you something ;-)

global lastObjectsDict
objects = gc.get_objects()

newObjects = [o for o in objects if not lastObjectsDict.has_key(id(o))]

lastObjectsDict = {}
for o in objects:
    lastObjectsDict[id(o)]=o

2. Keep track of the number of objects.

def printGc(message=None,onlyPrintChanges=false):

    if not debugGC: return None

    if not message:
        message = callerName(n=2) # Left as an exercise for the reader.

    global lastObjectCount

    try:
        n = len(gc.garbage)
        n2 = len(gc.get_objects())
        delta = n2-lastObjectCount
        if not onlyPrintChanges or delta:
            if n:
                print "garbage: %d, objects: %+6d =%7d %s" %
(n,delta,n2,message)
            else:
                print "objects: %+6d =%7d %s" %
(n2-lastObjectCount,n2,message)

        lastObjectCount = n2
        return delta
    except:
        traceback.print_exc()
        return None

3. Print lots and lots of info...

def printGcRefs (verbose=true):

    refs = gc.get_referrers(app().windowList[0])
    print '-' * 30

    if verbose:
        print "refs of", app().windowList[0]
        for ref in refs:
            print type(ref)
            if 0: # very verbose
                if type(ref) == type({}):
                    keys = ref.keys()
                    keys.sort()
                    for key in keys:
                        val = ref[key]
                        if isinstance(val,leoFrame.LeoFrame): # changes as
needed
                            print key,ref[key]
    else:
        print "%d referers" % len(refs)

Here app().windowList is a key data structure of my app.  Substitute your
own as a new argument.

Basically, Python will give you all the information you need.  The problem
is that there is way too much info, so you must experiment with filtering
it.  Don't panic: you can do it.

4. A totally different approach.  Consider this function:

def clearAllIvars (o):

    """Clear all ivars of o, a member of some class."""

    o.__dict__.clear()

This function will grind concrete walls into grains of sand.  The GC will
then recover each grain separately.

My app contains several classes that refer to each other.  Rather than
tracking all the interlocking references, when it comes time to delete the
main data structure my app simply calls clearAllIvars for the various
classes.  Naturally, some care is needed to ensure that calls are made in
the proper order.

HTH.

Edward
--------------------------------------------------------------------
Edward K. Ream   email:  edreamleo at charter.net
Leo: Literate Editor with Outlines
Leo: http://webpages.charter.net/edreamleo/front.html
--------------------------------------------------------------------






More information about the Python-list mailing list