You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:使用FigureCanvasTkAgg时Matplotlib内存泄漏无法回收

如何清理Tkinter应用中Matplotlib的内存占用?

我将Matplotlib官方的Tk嵌入示例代码放入循环后,程序内存占用持续飙升至1000MB。尝试了多种内存清理操作(包括清空图表、销毁组件等)均无法消除泄漏,仅通过复用图表而非每次新建能部分规避问题,但无法彻底解决。

通过tracemalloc检测发现,Python侧仅占用约1MB内存,泄漏源于C侧渲染器资源未被释放——即便已无对应引用。我使用的是Matplotlib 3.6.2版本(该版本本应修复同类泄漏问题),且在Windows系统的多个Python版本中均可复现此问题。

复现代码

import tkinter
import matplotlib
print(matplotlib._version.version)
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
import gc
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import psutil
import os, psutil
process = psutil.Process(os.getpid())
import numpy as np
import time

root = tkinter.Tk()
frame = tkinter.Frame(root)
root.wm_title("Embedding in Tk")
import matplotlib.pyplot as plt

def my_func():
    global root,frame
    fig = Figure(figsize=(5, 4), dpi=100)
    t = np.arange(0, 3, .01)
    ax = fig.add_subplot()
    line = ax.plot(t, 2 * np.sin(2 * np.pi * t))
    ax.set_xlabel("time [s]")
    ax.set_ylabel("f(t)")
    canvas = FigureCanvasTkAgg(fig, master=frame)  # A tk.DrawingArea.
    canvas.draw()

    # pack_toolbar=False will make it easier to use a layout manager later on.
    toolbar = NavigationToolbar2Tk(canvas, frame, pack_toolbar=False)
    toolbar.update()

    toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X)
    canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)
    time.sleep(0.1)
    
    # 我尝试过的内存清理操作
    ax = fig.axes[0]
    ax.clear()
    canvas.get_tk_widget().pack_forget()
    toolbar.pack_forget()
    canvas.figure.clear()
    canvas.figure.clf()
    canvas.get_tk_widget().destroy()
    toolbar.destroy()
    mem = process.memory_info().rss/2**20
    print(mem)  # 单位MB
    if mem > 1000:
        root.destroy()
    frame.destroy()
    frame = tkinter.Frame(root)
    root.after(10,my_func)
    gc.collect()

if __name__ == "__main__":
    root.after(1000,my_func)
    root.mainloop()

可行的解决方法

针对C侧渲染器未释放的问题,需要补充更彻底的资源解绑与引用清除步骤:

1. 优化清理流程,切断核心关联

在销毁组件前,显式切断Canvas与Figure的关联,手动删除所有相关引用,确保C侧资源能被正确回收:

# 优化后的内存清理代码段
# 1. 切断Canvas与Figure的绑定,释放C侧渲染器关联
canvas.figure = None
# 2. 销毁Tk组件
canvas.get_tk_widget().destroy()
toolbar.destroy()
# 3. 删除Python侧所有相关引用,避免引用计数阻碍回收
del ax, line, fig, canvas, toolbar
# 4. 处理Frame并强制触发垃圾回收
frame.destroy()
frame = tkinter.Frame(root)
gc.collect()
# 等待Tk事件循环完成销毁操作
root.update_idletasks()

2. 更换Backend尝试

若TkAgg后端仍存在泄漏,可尝试切换至其他后端,比如:

# 非交互场景可选Agg后端
matplotlib.use("Agg")
# 需交互则改用Qt后端
# matplotlib.use("QtAgg")

3. 复用核心对象(最彻底的规避方式)

避免每次循环新建Figure、Canvas等对象,初始化时创建一次,后续仅更新数据:

# 初始化阶段仅创建一次核心对象
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot()
line, = ax.plot([], [])
ax.set_xlabel("time [s]")
ax.set_ylabel("f(t)")
canvas = FigureCanvasTkAgg(fig, master=frame)
toolbar = NavigationToolbar2Tk(canvas, frame, pack_toolbar=False)
# 完成布局
toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X)
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)

def my_func():
    global line, ax
    # 仅更新数据与图表状态
    t = np.arange(0, 3, .01)
    line.set_data(t, 2 * np.sin(2 * np.pi * t))
    ax.relim()
    ax.autoscale_view()
    canvas.draw()
    time.sleep(0.1)
    mem = process.memory_info().rss/2**20
    print(mem)
    if mem > 1000:
        root.destroy()
    root.after(10, my_func)

内容的提问来源于stack exchange,提问作者Ahmed AEK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:15:51