【问题标题】:pygtk notebook page close memory leakpygtk 笔记本页面关闭内存泄漏
【发布时间】:2012-04-18 22:19:20
【问题描述】:

我有一个笔记本,其中包含带有标签的页面,其中有关闭按钮。当您单击按钮时,该选项卡中的页面将从笔记本中删除(关闭)。 问题是当标签页关闭时,内存永远不会被释放,因为似乎还有东西在引用被破坏的页面。

这是我笔记本的一些示例代码:

notebook = gtk.Notebook
def create_tab():
    page = gtk.Label( "THis is a page")
    page.show()
    tab = gtk.HBox()#The custom tab-label widget
    tab_label = gtk.Label( "Tab")
    tab_label.show()
    tab.pack_start( tab_label)
    tab_close = gtk.Button()
    tab_close.set_image(gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU))
    tab_close.connect( 'clicked', destroy_tab, page )
    tab_close.show()
    tab.pack_end( tab_close)
    tab.show()

    notebook.append_page(page, tab)
def destroy_tab( widget, data=None):
    page_num = notebook.page_num( data )
    notebook.remove_page( page_num )
    data.destroy()

create_tab 函数是添加到按钮单击信号的回调,因此我可以向笔记本添加任意数量的页面。但是当页面从笔记本中移除时,通过destroy_tab回调,页面成功从笔记本中移除,但内存从未释放。

这个link 概述了我遇到的同样问题。它暗示的一件事是,仍然存在通过设置为 tab_label 的自定义小部件对页面的引用。我也尝试过销毁自定义小部件,甚至递归地销毁其所有子小部件,但似乎仍然无法弄清楚仍然引用此页面以将其保存在内存中的内容。可能是什么问题?

【问题讨论】:

    标签: python gtk pygtk


    【解决方案1】:

    示例中唯一需要大量资源的是图像的 pixbuf。尝试使用后显式销毁 GtkImage。

    【讨论】:

    • 我已经试过了,事实上我递归地销毁了标签标签小部件的孩子......销毁了 GtkImage,按钮......一切。而且还是会出现这个问题。
    【解决方案2】:

    有趣的是,就在上周,我在how to detect reference count issues in python 上阅读了那篇博文。它可以帮助您找到谁在保留参考。

    【讨论】:

    • 感谢您的建议,我会看看它是否会导致我找到罪魁祸首。
    【解决方案3】:

    试试这个:

    data.destory()
    del data
    

    然后等待 python autogarbage 收集(它可能会在几秒钟后工作)

    【讨论】:

      【解决方案4】:

      我设法通过gc.getreferrers() 发现有一个循环引用 在关闭按钮和它正在关闭的页面之间。通过进一步的测试,我发现这不是我自己的错,但就像GtkNotebook 在将两者添加到笔记本时似乎将两者联系起来一样。我确实设法简单地摆脱了那个循环引用 通过创建一个包含对小部件本身的引用的自定义按钮,因此,当按钮被销毁时,该小部件引用设置为 None 并且似乎成功摆脱了该引用。 但是,这似乎仍然没有清除页面关闭后的内存。

      这是修改后的代码:

      class CloseButton(gtk.Button):
          def __init__(self, widget):
              gtk.Button.__init__(self)
              self.widget_ref = widget#Pass the widget reference to this button
              #Initialize the button here...
              self.connect( 'destroy', self.on_destroy )
          def on_destroy( self, widget ):
              self.widget_ref = None#Destroy the reference to the widget
      

      然后,在代码中你会像这样使用按钮:

      notebook = gtk.Notebook
      def create_tab():
          page = gtk.Label( "THis is a page")
          page.show()
          tab = gtk.HBox()#The custom tab-label widget
          tab_label = gtk.Label( "Tab")
          tab_label.show()
          tab.pack_start( tab_label)
          tab_close = CloseButton(page)#pass a reference to page
      
          tab_close.connect( 'clicked', destroy_tab )
          tab_close.show()
          tab.pack_end( tab_close)
          tab.show()
      
          notebook.append_page(page, tab)
      
      def destroy_tab( widget, data=None):
          page_num = notebook.page_num( widget.widget_ref )
          notebook.remove_page( page_num )
          widget.destroy()
      

      这就是诀窍!在按钮上调用destroy方法时引用消失

      【讨论】: