Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?
like image 938
fraxel Avatar asked Feb 17 '12 13:02

fraxel


People also ask

What is a weak reference in garbage collection?

The garbage collector cannot collect an object in use by an application while the application's code can reach that object. The application is said to have a strong reference to the object. A weak reference permits the garbage collector to collect the object while still allowing the application to access the object.

What is the purpose of garbage collection in programming?

They aren’t looking for things to delete, they know exactly what has to be removed. Garbage collection is a method to free the programmer from worrying about basic memory management. It isn’t the only option; simple reference counting also covers the vast majority of memory management needs.

How do you prevent garbage collection?

Garbage collection is performed automatically. We cannot force or prevent it. Objects are retained in memory while they are reachable. Being referenced is not the same as being reachable (from a root): a pack of interlinked objects can become unreachable as a whole, as we’ve seen in the example above.

What is the complexity of garbage collection?

Garbage collection has to scan memory, thus its complexity must be a function of N, where N may be the number of bytes or the number of objects. It must be bound by a linear function of N, since ultimately it needs to scan all of these objects.


1 Answers

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.

like image 159
Eli Bendersky Avatar answered Oct 04 '22 17:10

Eli Bendersky