fraxel
fraxel

Reputation: 35309

internal reference prevents garbage collection

I am writing a simple platform game, and I've found that when removing 'ghost' instances, they persist and are not garbage collected. It seems that although I am removing all references, the ghost objects have some kind of internal references that are preventing them being garbage collected. Specifically they have attributes which are method switches.

The following code illustrates my problem:

import weakref

weak_ghosts = weakref.WeakKeyDictionary()

class Ghost(object):
    def __init__(self):
        #pass
        self.switch = {'eat':self.eat, 'sleep':self.sleep}

    def eat(self):
        pass

    def sleep(self):
        pass

ghost = Ghost()
weak_ghosts[ghost] = None
#ghost.switch = {}    # uncomment this line and ghost is successfully removed
del ghost
print "number of ghosts =", len(weak_ghosts)

#output:
number of ghosts = 1

Questions:

  1. What is actually going on?
  2. What should I be doing to avoid this situation?
  3. Am I using the correct methodology for making a switchable dictionary of methods?

Upvotes: 7

Views: 764

Answers (2)

rodrigo
rodrigo

Reputation: 98476

An option is to do:

class Ghost(object):
    def __init__(self):
        self.switch = {'eat':Ghost.eat, 'sleep':Ghost.sleep}

So the methods are kept unbound.

Upvotes: 2

Eli Bendersky
Eli Bendersky

Reputation: 273646

There is a circular reference created by self.switch referencing the object it's part of. Check this out:

import weakref

class Ghost(object):
    def __init__(self):
        #pass
        self.switch = {'eat':self.eat, 'sleep':self.sleep}

    def eat(self):
        pass

    def sleep(self):
        pass

ghost = Ghost()

def callback(o):
    print 'callback', o

wref = weakref.ref(ghost, callback)
print 'del ghost'
del ghost
print 'after del ghost'

Prints:

del ghost
after del ghost
callback <weakref at 00B55FC0; dead>

So the actual object was just cleaned on shutdown.

You can run the GC manually to see the effect. Add this to the end of the script above:

print 'gc.collect'
import gc
gc.collect()
print 'after gc.collect'

Now you'll see:

del ghost
after del ghost
gc.collect
callback <weakref at 00B55FC0; dead>
after gc.collect

Note that by default, this GC is enabled and will run from time to time. It will clean up your ghost objects because they become unreachable circular references.

Upvotes: 4

Related Questions