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

Tkinter/CustomTkinter自定义组件内存泄漏问题排查求助

自定义Video组件的内存泄漏问题排查

我在Tkinter/CustomTkinter应用中使用自定义Video组件时遭遇内存泄漏问题。尽管已尝试手动删除所有成员变量、销毁组件,内存仍无法完全释放。以下是该自定义类的相关代码,希望找出问题所在:

import tkinter
import webbrowser
import customtkinter as ctk
from typing import List, Union, Tuple
from tkinter import PhotoImage
import pyperclip
from utils import (
    ValueConvertUtility,
)
from widgets.components.thumbnail_button import (
    ThumbnailButton,
)
from services import (
    ThemeManager, LanguageManager
)
from settings import (
    AppearanceSettings
)

class Video(ctk.CTkFrame):
    """A class representing a video widget."""

    default_thumbnails: Tuple[tkinter.PhotoImage, tkinter.PhotoImage] = (None, None)

    def __init__(
            self,
            root: ctk.CTk,
            master: Union[ctk.CTkFrame, ctk.CTkScrollableFrame],
            width: int = 0,
            height: int = 0,
            # video info
            video_url: str = "",
            video_title: str = "-------",
            channel: str = "-------",
            thumbnails: List[PhotoImage] = (None, None),
            channel_url: str = "-------",
            length: int = 0):

        super().__init__(
            master=master,
            height=height,
            width=width,
            border_width=1,
            corner_radius=8,
        )
        self.root = root
        self.master = master
        self.height: int = height
        self.width: int = width
        # video details
        self.video_url: str = video_url
        self.video_title: str = video_title
        self.channel: str = channel
        self.channel_url: str = channel_url
        self.length: int = length
        self.thumbnails: List[PhotoImage] = thumbnails
        # widgets
        self.info_frame: Union[ctk.CTkFrame, None] = None
        self.url_label: Union[ctk.CTkLabel, None] = None
        self.video_title_label: Union[ctk.CTkLabel, None] = None
        self.channel_btn: Union[ctk.CTkButton, None] = None
        self.video_length_label: Union[ctk.CTkLabel, None] = None
        self.thumbnail_btn: Union[ThumbnailButton, None] = None
        self.remove_btn: Union[ctk.CTkButton, None] = None

        from widgets import ContextMenu
        self.context_menu: Union['ContextMenu', None] = None
        # initialize the object
        self.create_widgets()
        self.set_widgets_texts()
        self.set_widgets_sizes()
        self.set_widgets_fonts()
        self.set_widgets_colors()
        self.set_tk_widgets_colors()
        self.set_widgets_accent_color()
        self.place_widgets()
        self.bind_widgets_events()

        # register to Theme Manger for accent color updates & widgets colors updates
        ThemeManager.register_widget(self)
        LanguageManager.register_widget(self)

    """
    other methods
    """

    def __del__(self):
        """Clear the Memory."""
        del self.height
        del self.width
        
        # video details
        del self.video_url
        del self.video_title
        del self.channel
        del self.channel_url
        del self.length
        del self.thumbnails

        # widgets
        del self.info_frame
        del self.url_label
        del self.video_title_label
        del self.channel_btn
        del self.video_length_label
        del self.remove_btn

        del self.context_menu
        del self.thumbnail_btn

        del self

    def destroy_widgets(self):
        """Destroy the child widget."""
        self.video_length_label.destroy()
        self.info_frame.destroy()
        self.video_title_label.destroy()
        self.channel_btn.destroy()
        self.url_label.destroy()
        self.remove_btn.destroy()
        self.destroy()

    def kill(self):
        """Destroy the widget."""
        ThemeManager.unregister_widget(self)
        LanguageManager.unregister_widget(self)
        self.pack_forget()
        self.destroy_widgets()
        self.__del__()

问题分析

  • 错误调用__del__方法:Python的__del__是垃圾回收器自动触发的,手动调用不仅无效,还会导致对象引用关系混乱,阻碍正常回收流程。kill方法里的self.__del__()完全多余。
  • destroy_widgets重复销毁:self.destroy()(CTkFrame的父类方法)会自动递归销毁所有子组件,手动逐个销毁子组件属于重复操作,可能留下无效引用。
  • PhotoImage引用未彻底清理:Tkinter的PhotoImage对象如果被多个组件(比如ThumbnailButton)持有引用,仅删除self.thumbnails无法释放内存。另外类变量default_thumbnails若长期持有图片对象,也会占用内存。
  • 管理器引用残留:若ThemeManager或LanguageManager的unregister_widget方法未彻底移除对当前Video对象的引用(比如内部列表未执行实际删除),垃圾回收器无法回收该对象。
  • __del__中的del self无效:在对象自身的__del__方法中删除自身是不允许的,会引发不可预测的行为。

修复建议

  1. 移除手动调用__del__的代码:删掉kill方法里的self.__del__(),让垃圾回收器自动处理。
  2. 简化destroy_widgets方法:
def destroy_widgets(self):
    """Destroy the widget and its children"""
    self.destroy()
  1. 清理PhotoImage引用:
    • 确保ThumbnailButton在销毁时清空对图片的引用,比如在其destroy方法中设置self.image = None;
    • 若self.thumbnails是内部创建的,销毁前可设置self.thumbnails = None,帮助垃圾回收器识别。
  2. 验证管理器的引用清理:检查ThemeManager.unregister_widget和LanguageManager.unregister_widget的实现,确保它们从内部存储(如列表、字典)中完全移除了当前对象的引用。
  3. 删除无效的__del__方法:手动删除成员变量对垃圾回收无意义,Python会自动处理这些变量的回收,直接删除整个__del__方法。
  4. 优化kill方法:
def kill(self):
    """Destroy the widget and clean up references"""
    ThemeManager.unregister_widget(self)
    LanguageManager.unregister_widget(self)
    self.pack_forget()
    self.destroy()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 06:56:06