Garbage collection of recursive inner function

Diez B. Roggisch deets at nospam.web.de
Mon Aug 4 14:08:29 EDT 2008


from.future.import at gmail.com schrieb:
> Hi,
> 
> I encountered garbage collection behaviour that I didn't expect when
> using a recursive function inside another function: the definition of
> the inner function seems to contain a circular reference, which means
> it is only collected by the mark-and-sweep collector, not by reference
> counting. Here is some code that demonstrates it:
> 
> ===
> def outer():
> 
> 	def inner(n):
> 		if n == 0:
> 			return 1
> 		else:
> 			return n * inner(n - 1)
> 
> 	return 42
> 
> import gc
> gc.set_debug(gc.DEBUG_SAVEALL)
> print outer()
> gc.collect()
> print gc.garbage
> ===
> 
> Output when executed:
> $ python internal_func_gc.py
> 42
> [<cell at 0xb7dec3ec: function object at 0xb7dbd3ac>, (<cell at
> 0xb7dec3ec: function object at 0xb7dbd3ac>,), <function inner at
> 0xb7dbd3ac>]
> 
> Note that the inner function is not called at all, it is only defined.
> If the inner function is moved outside the scope of the outer
> function, gc.garbage will be empty. If the inner function is inside
> but not recursive, gc.garbage will also be empty. If the outer
> function is called twice, there will be twice as many objects in
> gc.garbage.
> 
> Is this expected behaviour? Collecting an object when its refcount
> reaches zero is preferable to collecting it with mark-and-sweep, but
> maybe there is a reason that a circular reference must exist in this
> situation. I want to check that first so I don't report a bug for
> something that is not a bug.

The reference comes from the closure of inner. And inner is part of the 
closure, so there is a circular reference.

I don't see a way to overcome this - consider the following code:

def outer():

   def inner():
       inner()

   if random.random() > .5:
      return inner


How is the GC/refcounting to know if it can create a reference or not?



Diez



More information about the Python-list mailing list