gc.garbage from paramiko

Andrew Bennetts andrew at canonical.com
Mon Feb 6 08:09:24 GMT 2006


On Fri, Feb 03, 2006 at 10:41:11AM -0800, Robey Pointer wrote:
> 
> On 2 Feb 2006, at 17:19, Andrew Bennetts wrote:
> 
> >When I run tests for some code I have here that uses bzr, the test  
> >runner (Zope
> >3's) warns me after each test that there's garbage in gc.garbage  
> >(i.e. cycles
> >that Python's garbage collector can't collect).  After the first  
> >test, it's
> >just:
> >
> >[<paramiko.Transport at 0xB5DBD44CL (unconnected)>,  
> ><paramiko.Packetizer object at 0xb72d006c>, <paramiko.Transport at  
> >0xB5D0672CL (unconnected)>, <paramiko.Packetizer object at  
> >0xb5d0682c>]
> 
> Do you have any way to reproduce this outside of Zope?  I tried  
> running the bzr selftests (and a few other tests, like 'bzr push' to  
> an sftp site) with 'python -i' and then doing gc.collect() and  
> looking in gc.garbage, but wasn't able to reproduce it.

I'm working on this.  I'll let you know when I've got something minimal and
complete I can share with you.

> >The root cause is that paramiko is using __del__.  This is almost  
> >always a bad
> >idea, because objects with __del__ cannot be garbage collected if  
> >they're in a
> >reference cycle.  It's an almost certain way to cause memory leaks,  
> >unless
> >you're *extremely* careful to always break cycles manually.
> >
> >I think paramiko should not use __del__.
> 
> Unfortunately __del__ is needed for some python-file emulation, which  
> bzr and other apps rely on heavily, often without knowing (which is  
> the point).  But it should be okay as long as no cycles are created.   
> But yes, I agree that use of __del__ is really a black art.

What do you need to do on __del__?  socket.sockets already close themselves just
like files.

Cycles are really, really hard to avoid.  If a traceback occurs, that can create
cycles.  There's lots of ways to unexpectedly make a cycle.

> Paramiko actually goes to some trouble to scrupulously avoid cycles  
> with __del__.  Obviously there's a bug. :)  In your case, something  
> is creating a link from the Packetizer back to the Transport, which  
> shouldn't ever happen.  The Packetizer has no need to ever refer to  
> the Transport.  So I'd love to find a reproducable case and figure  
> out how that reference is getting attached.

There's a pretty obvious cycle right here:

        self.packetizer.set_keepalive(interval,
            lambda x=self: x.global_request('keepalive at lag.net', wait=False))

The callback you pass is a lambda with a reference to the packetizer, and I
don't see anywhere that this is broken.

I'll work on a reproducible case of my problem, but I'd like to hear more about
what you need __del__ for.

-Andrew.





More information about the bazaar mailing list