前言
最近开发redis可视化ui,连到了阿里云上的redis,这个redis的hash动辄好几百条数据,拿到后写到QTableview中。测试良好。
但测试一段时间后,点了几十条hash,拿到了几十条hash数据,QTableview也更新了几十次,在进行其他操作的时候,出现了程序崩溃。
于是就想,是不是发生了内存泄漏。
场景
假设有一个QTableview,名叫table
先看下面的代码,功能是清空TableView
void clearTreeView(){
ui->view->setModel(nullptr);
}
再看下面的代码,功能是设置数据
void setTableData(){
QStandardItemModel* model = new QStandardItemModel();
ui->view->setModel(model);
}
可以看到,设置数据时,QStandardItemModel为指针,new了一个对象,但当我们清空时候,原来的model会不会被delete呢?
我们来验证一下。
验证
新建一个项目,界面是这样的
为了验证是否释放内存,我们新建一个QObject类,名叫QModel,继承QStandardItemModel,重写析构函数。
头文件qmodel.hpp:
#ifndef QMODEL_HPP
#define QMODEL_HPP
#include <QObject>
#include <QStandardItemModel>
class QModel : public QStandardItemModel
{
Q_OBJECT
public:
explicit QModel(QObject *parent = nullptr);
~QModel();
signals:
};
#endif // QMODEL_HPP
头文件实现文件qmodel.cpp
#include "qmodel.hpp"
#include <QDebug>
QModel::QModel(QObject *parent) : QStandardItemModel(parent)
{
}
QModel::~QModel()
{
// 析构函数
qDebug() << "model delete";
}
ui窗口连接好信号槽,分为加载数据和清空数据
这里每按一次加入数据按钮,就添加1-10000数字。
table view控件名叫tableView
#include "mainwindow.hpp"
#include "ui_mainwindow.h"
#include <qmodel.hpp>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 添加数据信号槽,lambda函数写法
connect(ui->pushButton, &QPushButton::clicked, this, [=](){
// 继承QStandardItemModel的那个类
QModel* model = new QModel();
for(int i = 0; i < 10000; i++){
model->appendRow(new QStandardItem(QString::number(i)));
}
ui->tableView->setModel(model);
});
connect(ui->pushButton_2, &QPushButton::clicked, this, [=](){
ui->tableView->setModel(nullptr);
});
}
MainWindow::~MainWindow()
{
delete ui;
}
打开任务管理器。
运行程序。
可以看到,程序最开始所占内存大小
9396K。
后面就不放图了,这里只说数据。
我们来加入数据
此时内存占用变为12724K,显著提高。
我们来清空数据。
此时内存占用12932K,不减反增。
析构函数按理会打印model delete字样,但是qtcreator控制台什么也没发生,很显然,数据清空后,资源没有释放,发生了内存泄漏。
再点击加入数据。
内存占用15100K,变得更多了。
清空。
内存15228K。
结论
在通过setModel(nullptr)方式清空表格数据时,原来表格内的数据model并不会主动释放,会仍存在于内存中,造成了内存泄漏,如果我们长时间运行软件,频繁更新数据模型,最终会导致程序崩溃。
想想,一个小破软件,占用内存好几个G。感人。
解决
我们在重新setModel时,需要手动delete原来的数据模型。
通过model方法可以拿到model
void setData(){
delete ui->tableView->model();
ui->tableView->setModel(nullptr); // 或新model
}
此时在运行。
加入数据,12756K。
清空,11132K。
加入数据:13184K。
清空:11720K。
反复横跳。
另外,在QStandardItemModel被析构时,其内部存储的QStandardItem是否会释放,官方文档对QStandardItemModel有下列描述:
[virtual] QStandardItemModel::~QStandardItemModel()
Destructs the model. The model destroys all its items.
销毁模型,模型会销毁所有的项目。