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