QTableView关联窗口关闭后内存泄漏问题排查求助
PyQt6 表格窗口关闭后内存泄漏问题分析与解决
问题描述
主窗口点击按钮可打开内嵌10万行数据QTableView的子窗口,关闭子窗口后内存无法回到初始水平(启动时10.9MB→打开后256.8MB→关闭后82.4MB),重复操作内存持续增长。尝试销毁对象、设置表格为None、清空表格数据等操作均无效。
内存泄漏原因分析
- 对象引用未释放:主窗口的
self.table_window属性始终持有子窗口对象,即使调用deleteLater(),只要该引用存在,Python垃圾回收(GC)就无法彻底回收子窗口及其内部组件的内存。 - 模型资源未彻底清理:仅调用
removeRows()清空表格行,QStandardItemModel本身的内存未被释放,且模型与视图的关联未解除,导致部分资源残留。 - 信号连接未断开:子窗口的
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
相关产品推荐
相关产品推荐

