We encountered this problem in our project (gramps-project.org).  It
took quite a bit of work to remove the refs to allow GC. Basically, we
added code to track the callbacks then, when the objects were deleted,
added code to explicitly remove them (delete them or set them to None)
to break the cyclical refs.  Also, we added code to delete (or set to
None) attributes that might possibly be involved in the cycle.

It was a lot of work but we eventually stopped the memory leaks.
Still, I believe that pygtk could be a LOT smarter in this regard.  I
think that what we had to do amounts to fixing a serious design issue
in pygtk.

On Tue, Dec 21, 2010 at 12:16 PM, Pierre <[email protected]> wrote:
> Hello list,
>
> I run into a surprising behavior regarding garbage collection. In the
> following example, a Notebook page is removed from its parent widget and
> destroyed. However, the page is not garbage-collected unless the "tab"
> widget (a gtk.HBox) is first destroyed.
>
> Only the Page instance refers to the "tab" widget. Besides, "tab" is
> indirectly associated to the page: one of its child widget's signal is
> connected to a Page method. So we have something like this:
>
>  page --> tab --> button --> callback --> page --> tab --> etc.
>
> Once remove_page() returns, I would expect tab and page to be destroyed and
> collected, because both objects become unreachable (unreachable through
> Python variables and GTK calls). But the gc module shows that they are not
> collected.
>
> Is this the expected behavior ?  I'm using pygtk 2.17.0, gtk 2.20.1, and
> Python 2.6.6.
>
> Thank you for your time.
>
> Pierre
>
>
> # ------------------------------------------------------ #
>
> import gc
> import gtk
> import gobject
>
> DESTROY_TAB = False
>
> class Page(gtk.VBox):
>
>    def __init__(self):
>        gtk.VBox.__init__(self)
>        self.pack_start(gtk.TextView(), True, True) # To fill the window
>        button = gtk.Button()
>        button.connect("clicked", self.hello)
>        title = gtk.Label("hello")
>        tab = gtk.HBox()
>        tab.pack_start(title, True, True)
>        tab.pack_end(button, False, False)
>        tab.show_all()
>
>        # Keeping a reference here is the culprit. Could it be a
>        # circular reference problem ?
>        # tab --> button --> hello --> page --> tab --> ...
>        self.tab = tab
>
>    def hello(self, widget):
>        print "hello"
>
> def add_page(notebook):
>    print "Adding a page to the Notebook."
>    page = Page()
>    page.show_all()
>    notebook.append_page(page, tab_label=page.tab)
>
> def remove_page(notebook):
>    print "Removing the page."
>    page = notebook.get_nth_page(0)
>    notebook.remove_page(0)
>    page.destroy()
>    # Destroying page.tab let the GC collect the page.
>    if DESTROY_TAB:
>        page.tab.destroy()
>
> def main():
>    notebook = gtk.Notebook()
>    w = gtk.Window()
>    w.add(notebook)
>    w.resize(400, 400)
>    w.show_all()
>    w.connect("destroy", gtk.main_quit)
>    gobject.idle_add(add_page, notebook)
>    gobject.timeout_add(1000, remove_page, notebook)
>    gobject.timeout_add(2000, gtk.main_quit)
>    gtk.main()
>
> def seek_page():
>    gc.collect()
>    oo = gc.get_objects()
>    for o in oo:
>        if hasattr(o, "__class__") and (o.__class__ is Page
>                                        or o.__class__ is gtk.HBox):
>            print
>            print o, "escaped garbage collection"
>            print 'Referrers are :'
>            for r in gc.get_referrers(o):
>                print '  *', repr(r)[:65], '...'
>
>
> main()
> seek_page()
>
>
> # Output:
> # ------
> #
> # Adding a page to the Notebook.
> # Removing the page.
> #
> # <Page object at 0x98a898c (GtkVBox at 0x9960c18)> escaped garbage
> collection
> # Referrers are :
> #   * [(), {'__setattr__': <slot wrapper '__setattr__' of 'object' obje ...
> #   * <bound method Page.hello of <Page object at 0x98a898c (GtkVBox at ...
> #   * <frame object at 0x999f68c> ...
> #
> # <gtk.HBox object at 0x98a8a04 (GtkHBox at 0x9960c70)> escaped garbage
> collection
> # Referrers are :
> #   * [(), {'__setattr__': <slot wrapper '__setattr__' of 'object' obje ...
> #   * <frame object at 0x999f68c> ...
> #   * {'tab': <gtk.HBox object at 0x98a8a04 (GtkHBox at 0x9960c70)>} ...
> _______________________________________________
> pygtk mailing list   [email protected]
> http://www.daa.com.au/mailman/listinfo/pygtk
> Read the PyGTK FAQ: http://faq.pygtk.org/
>



-- 
Gerald Britton
_______________________________________________
pygtk mailing list   [email protected]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://faq.pygtk.org/

Reply via email to