Reputation: 35309
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:
Upvotes: 7
Views: 764
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
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