一、问题背景:浏览图片时 UI 卡顿,内存逐渐增加
在对一款基于 Qt 的文件阅读 UI 应用进行调试时,发现:
- 每次点击切换页面,有图片重新加载操作,UI 系统开始卡顿
- 使用 top 查看,RES 内存持续上涨,即使 CPU 使用率不高
- 应用运行 1-2 分钟后,系统突然卡死或者被 OOM 杀掉
类似问题应用很普遍。本文将分析一个简单示例,说明这类问题如何发生,如何确诊与优化。
二、原始代码:QPixmap 未 delete 导致泄露
// 错误用法:每次加载都新建一个 QPixmap
QPixmap *pix = new QPixmap("/tmp/img1.png");
label->setPixmap(*pix); // 未 delete pix
视觉上看不出问题,但实际上每次操作后旧的 QPixmap
已经无法释放,最终导致 heap 内存爆表。
三、完整示例:Qt5/Qt6 UI 应用演示
文件名:leaky_pixmap_demo.cpp
#include <QApplication>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QPixmap>
#include <QWidget>
class LeakyWindow : public QWidget {
Q_OBJECT
public:
LeakyWindow(QWidget *parent = nullptr) : QWidget(parent) {
QVBoxLayout *layout = new QVBoxLayout(this);
label = new QLabel("\u70b9\u51fb\u6309\u94ae\u52a0\u8f7d\u56fe\u7247", this);
layout->addWidget(label);
QPushButton *btn = new QPushButton("\u52a0\u8f7d\u56fe\u7247", this);
layout->addWidget(btn);
connect(btn, &QPushButton::clicked, this, &LeakyWindow::loadImage);
}
private slots:
void loadImage() {
QPixmap *pix = new QPixmap("/tmp/img1.png"); // 未 delete
label->setPixmap(*pix); // 每次切换都有泄露
}
private:
QLabel *label;
};
#include "leaky_pixmap_demo.moc"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
LeakyWindow win;
win.setWindowTitle("QPixmap Leak Demo");
win.show();
return app.exec();
}
编译命令
g++ leaky_pixmap_demo.cpp -o leaky_pixmap_demo $(pkg-config --cflags --libs Qt5Widgets)
四、验证泄露效果:切换图片内存持续上涨
运行后,多次点击 “加载图片” 按钮:
top -p $(pidof leaky_pixmap_demo)
观察 RES 内存不断上涨,最终系统卡顿/崩溃
五、正确使用方法
方案 1:使用栈上对象
QPixmap pix("/tmp/img1.png");
label->setPixmap(pix); // 自动释放
方案 2:维护成员变量,手动 delete
if (last_pix) delete last_pix;
last_pix = new QPixmap("/tmp/img1.png");
label->setPixmap(*last_pix);
六、问题解析思路:如果面试被问到,如何分析和回答?
接口例:
“UI 卡顿最可能的原因是什么?如果是内存问题,你怎么查看?”
回答结构:
-
先看硬挂、CPU 、IO (top/查指标),确诊为内存问题
-
解析确诊路径:
- 应用应用 malloc/new 运行时持续增长
- 别忘了基于 Qt 系统自带的 QPixmap / QImage / QStringList 等需要手动释放
-
用 top + pmap + valgrind 确定泄露点
-
解决方案:
- 转换为栈上对象,或维护类成员变量,精确控制生命周期
结论:UI 系统应用中内存泄露是需要特别讲解和调试的核心区域
- 一行 new 就可能导致系统纯电路卡死;
- 内核的 slab/cache 和用户的 heap 有相关联,需要全链路解析
- 查 /proc/meminfo + 监控 top 折展重要基本技巧