garbage collection / cyclic references

Aaron Brady castironpi at gmail.com
Sat Mar 21 12:15:43 EDT 2009


On Mar 21, 10:28 am, Aaron Brady <castiro... at gmail.com> wrote:
> On Mar 21, 9:50 am, "andrew cooke" <and... at acooke.org> wrote:
>
>
>
> > Aaron Brady wrote:
> > > On Mar 21, 7:54 am, "andrew cooke" <and... at acooke.org> wrote:
> > >> they should not be used to do things like flushing and closing
> > >> files, for example.
> > > What is your basis for this claim, if it's not the mere unreliability
> > > of finalization?  IOW, are you not merely begging the question?
>
> > I'm not sure it's clear, but I was talking about Java.
>
> > As Paul implied, a consequence of completely automated garbage management
> > is that it is (from a programmer's POV) deterministic.  So it's a
> [indeterministic]
> > programming error to rely on the finalizer to free resources that don't
> > follow that model (ie any resource that's anything other that
> [than]
> > reasonable
> > amounts of memory).
>
> > That's pretty much an unavoidable consequence of fully automated garbage
> > collection.  You can pretend it's not, and try using finalizers for other
> > work if you want.  That's fine - it's your code, not mine.  I'm just
> > explaining how life is.
>
> > Andrew
>
> My point is, that garbage collection is able to detect when there are
> no program-reachable references to an object.  Why not notify the
> programmer (the programmer's objects) when that happens?  If the
> object does still have other unreachable references, s/he should be
> informed of that too.
snip

I took the liberty of composing a sample cyclic reference detector.  I
will post the class definition later on in the discussion (when and
if).

The 'run' method resets the globals to a sample graph, as
illustrated.  'p' and 's' start out with one simulated program-visible
reference each.  As you see, the details are already long and boring
(yum).  I added comments post-facto.

>>> run() #only decref 'p'
p: (q), q: (pr), r: (q), s: (p)
>>>
>>> p.decref() #not safe to delete
{<p,2>: 1, <q,2>: 0, <r,1>: 0}
>>>
>>>
>>> run() #decref 'p' then 's'
p: (q), q: (pr), r: (q), s: (p)
>>>
>>> p.decref()
{<p,2>: 1, <q,2>: 0, <r,1>: 0}
>>>
>>> s.decref()
{<s,0>: 0, <p,2>: 0, <r,1>: 0, <q,2>: 0}
<s,0> ALL zero #'s' safe to delete
{<p,1>: 0, <q,2>: 0, <r,1>: 0}
<p,1> ALL zero #also deletes 'p', also safe
finalizing <s,0>
>>>
>>>
>>> run()
p: (q), q: (pr), r: (q), s: (p)
>>>
>>> s.decref()
{<s,0>: 0, <p,3>: 1, <r,1>: 0, <q,2>: 0}
{<p,2>: 1, <q,2>: 0, <r,1>: 0}
finalizing <s,0> #deletion
>>>
>>> p.decref()
{<p,1>: 0, <q,2>: 0, <r,1>: 0}
<p,1> ALL zero #'p' safe to delete
>>>
>>>
>>> run()
p: (q), q: (pr), r: (q), s: (p)
>>>
>>> s.decref()
{<s,0>: 0, <p,3>: 1, <q,2>: 0, <r,1>: 0}
{<p,2>: 1, <q,2>: 0, <r,1>: 0}
finalizing <s,0> #'p' not safe, reference still visible

We notice the duplicate 'all zero' indicator on run #2.  The cycle
detector ran on 's.decref', then 's' called 'p.decref', then the cycle
detector ran on that.  'q' and 'r' are safe to delete on runs 2 and 3.

Here is the implementation of 'final':

    def final( self ):
        for _, v in self.__dict__.items( ):
            if not isinstance( v, G ):
                continue
            v.decref( )
        print( 'finalizing', self )

The object should be asked to finish its references (cyclic only?),
but remain alive.  The programmer should see that the state is
consistent.  Later, its __del__ will be called.

We can decide that '__leave_reachability__' will be called without
nesting; and/or that '__del__' will be called without nesting, by
breaking finalization in to two steps.

FTR, this makes __leave_reachability__ about the equivalent of
tp_clear, since tp_traverse is prior defined for user-defined types.



More information about the Python-list mailing list