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

QTableView关联窗口关闭后内存泄漏问题排查求助

PyQt6 表格窗口关闭后内存泄漏问题分析与解决

问题描述

主窗口点击按钮可打开内嵌10万行数据QTableView的子窗口,关闭子窗口后内存无法回到初始水平(启动时10.9MB→打开后256.8MB→关闭后82.4MB),重复操作内存持续增长。尝试销毁对象、设置表格为None、清空表格数据等操作均无效。

内存泄漏原因分析

  1. 对象引用未释放:主窗口的self.table_window属性始终持有子窗口对象,即使调用deleteLater(),只要该引用存在,Python垃圾回收(GC)就无法彻底回收子窗口及其内部组件的内存。
  2. 模型资源未彻底清理:仅调用removeRows()清空表格行,QStandardItemModel本身的内存未被释放,且模型与视图的关联未解除,导致部分资源残留。
  3. 信号连接未断开:子窗口的winClosed信号与主窗口cleanup方法保持连接,可能导致对象被额外引用,阻碍回收。

修复方案

需从解除引用、清理模型、断开信号三个维度处理:

  • 在cleanup方法中断开信号连接,并将self.table_window置为None,解除主窗口对子窗口的持有
  • 清空表格数据后,手动删除模型对象并解除模型与视图的关联
  • 调整子窗口closeEvent逻辑,避免传递对象导致额外引用

修改后的完整代码

import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QTableView
from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QStandardItemModel, QStandardItem

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt6 App")
        self.resize(200, 100)

        self.button = QPushButton("Open Table")
        self.button.clicked.connect(self.open_table)

        layout = QVBoxLayout()
        widget = QWidget()
        layout.addWidget(self.button)
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.table_window = None  # 初始化属性

    def open_table(self):
        if self.table_window is not None:
            self.table_window.close()
        self.table_window = TableMainWindow()    
        self.table_window.show()                 
        self.table_window.winClosed.connect(self.cleanup)
        
    def cleanup(self):  
        # 断开信号连接
        self.table_window.winClosed.disconnect(self.cleanup)
        # 清理表格模型
        self.table_window.table.clear_and_release_model()
        # 删除子窗口并解除引用
        self.table_window.deleteLater()
        self.table_window = None  # 关键:解除持有引用

class TableMainWindow(QMainWindow):
    winClosed = pyqtSignal()  # 无需传递对象,避免额外引用

    def __init__(self, parent=None):
        super().__init__(parent)
        self.table = Table()
        self.setup_ui()

    def closeEvent(self, event):
        self.winClosed.emit()
        event.accept()

    def setup_ui(self):
        self.setGeometry(400, 50, 600, 500)
        central_widget = QWidget()
        main_layout = QVBoxLayout(central_widget)
        main_layout.addWidget(self.table)
        self.setCentralWidget(central_widget)


class Table(QTableView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.columns = ["col1", "col2", "col3", "col4", "col5"]
        data = [{col: "Sample Cell Text" for col in self.columns} for i in range(100000)]
        self.build_table()
        self.load_data(data)
        
    def build_table(self):
        self.model = QStandardItemModel(self)
        self.setModel(self.model)
        self.model.setColumnCount(len(self.columns))
        self.model.setHorizontalHeaderLabels(self.columns)

    def load_data(self, data):
        for row in range(len(data)):
            self.insert_row(row, data[row])
            
    def insert_row(self, row, data):
        items = [QStandardItem(str(data[col])) for col in self.columns]
        self.model.insertRow(row, items)

    def clear_and_release_model(self):
        # 清空模型数据并释放模型
        self.model.clear()
        self.setModel(None)  # 解除视图与模型的关联
        del self.model  # 删除模型对象,触发GC回收

def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    main()

验证效果

修改后重复打开关闭子窗口,内存占用会稳定在合理范围,不会持续增长。关闭子窗口后内存会回到接近初始启动的水平(具体数值可能因系统环境略有差异)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 12:24:58