【问题标题】:tkinter, matplotlib: memory usagetkinter,matplotlib:内存使用情况
【发布时间】:2017-11-01 04:50:18
【问题描述】:

我有一个使用tkinter 创建GUI 的简单程序。 GUI 包含一个按钮,每次按下它都会创建一个新的 tkinter Toplevel 窗口。每个顶层窗口都包含一个从自定义 Figure 类创建的 matplotlib 图。

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from Tkinter import *

class MainGUI(Tk):
    def __init__(self):
        Tk.__init__(self)
        Button(self, text="new_child", command=self.new_child).pack()
        self.mainloop()

    def new_child(self):
        self.tl = TL()

class TL(Toplevel):
    def __init__(self, **kw):
        Toplevel.__init__(self, **kw)
        self.data = "00000000000"*10000000  # just some data to analyze memory allocation 
        self._figure = MyFigure(self.data)
        self._canvas = FigureCanvasTkAgg(self._figure, master=self)
        self._canvas.get_tk_widget().grid(row=0, column=0, sticky="NSWE")

class MyFigure(Figure):
    def __init__(self, data):
        super(MyFigure, self).__init__()
        self._data = data

if __name__ == '__main__':
    MainGUI()

程序按预期运行,唯一的问题是关闭窗口不会释放任何内存。

当从顶层窗口中删除Figure 时,使用的内存会被正确释放,所以我认为Figure 会导致内存泄漏。

我读到引用计数不适用于使用 matplotlibs pyplot 接口创建的 Figure 对象,但这不适用于我的示例。 (详见here

我不明白这里发生了什么,所以任何帮助将不胜感激。

提前致谢。

编辑

我忘了提到我已经尝试使用gc.collect() 手动调用垃圾收集器,但这并没有帮助。

【问题讨论】:

  • 你是如何进行测试的?假设对象不再使用,我希望垃圾收集器最终会清理内存。
  • 我已经尝试手动调用 gc,但这没有帮助。测试非常简单。打开/关闭子窗口,您会看到程序使用的内存随着每个创建的子窗口而增加。
  • 能不能不覆盖TL类的destroy方法,在销毁窗口之前删除图形?

标签: python python-2.7 memory matplotlib tkinter


【解决方案1】:

尝试调用gc.collect()顶级窗口已关闭:

def new_child(self):
    tl = TL()
    self.wait_window(tl)   # Wait for tl to be closed before continuing execution
    del tl
    gc.collect()

【讨论】:

    【解决方案2】:

    感谢您提出这个问题,感谢您的回答对我有很大帮助。我发现自己处于类似的情况,但 GUI 稍微复杂一些。尽管应用了此处给出的解决方案,但我还是遇到了内存泄漏。似乎向 Tkinter 变量添加 跟踪(例如 IntVar、DoubleVar 等)会阻止 Toplevel 窗口被正确破坏,并导致泄漏。 在销毁时手动删除痕迹可修复此问题。让我举一个遭受泄漏的代码示例,以及我找到的修复程序。我希望它可以帮助其他人。

    初步: 首先,使用 Josselin 给出的new_child 的修正版:

    def new_child(self):
        tl = TL()
        self.wait_window(tl)   # Wait for tl to be closed before continuing execution
        del tl
        gc.collect()
    

    然后,代码内存泄漏(由跟踪引起):

    class TL(Toplevel):
        def __init__(self, **kw):
            Toplevel.__init__(self, **kw)
            self.number = IntVar()
            self.data = "00000000000"*10000000  # just some data to analyze memory allocation 
            self._figure = MyFigure(self.data)
            self._canvas = FigureCanvasTkAgg(self._figure, master=self)
            self._canvas.get_tk_widget().grid(row=0, column=0, sticky="NSWE")
            Spinbox(self, from_=0, to=10, textvariable=self.number).grid(row=1,column=0)
            self.number.trace('w', self.updateSomething)
        def updateSomething(self, *args): return
    

    最后,修复(手动删除销毁的痕迹):

    class TL(Toplevel):
        def destroy(self):
            for var, s, trid in self.allTraces: var.trace_vdelete(s, trid) #manually removes traces. Otherwise: memory leak!
            super(TL, self).destroy()
        def __init__(self, **kw):
            Toplevel.__init__(self, **kw)
            self.allTraces = []
            self.number = IntVar()
            self.data = "00000000000"*10000000  # just some data to analyze memory allocation 
            self._figure = MyFigure(self.data)
            self._canvas = FigureCanvasTkAgg(self._figure, master=self)
            self._canvas.get_tk_widget().grid(row=0, column=0, sticky="NSWE")
            Spinbox(self, from_=0, to=10, textvariable=self.number).grid(row=1,column=0)
            trid = self.number.trace('w', self.updateSomething)
            self.allTraces.append((self.number, 'w', trid))
        def updateSomething(self, *args): return
    

    【讨论】:

      猜你喜欢
      • 2013-11-07
      • 1970-01-01
      • 1970-01-01
      • 2015-02-03
      • 2012-05-30
      • 2021-02-26
      • 2010-10-24
      • 2015-06-14
      • 2015-01-06
      相关资源
      最近更新 更多