如何销毁 QPainter 对象/摆脱 drawText() 内存泄漏?

Posted

技术标签:

【中文标题】如何销毁 QPainter 对象/摆脱 drawText() 内存泄漏?【英文标题】:How do I destroy the QPainter object / Get rid of drawText() memory leak? 【发布时间】:2012-03-13 07:52:32 【问题描述】:

我正在尝试减少大型应用程序的内存泄漏。使用 valgrind,在调用 QPainter 类的 drawText() 函数时,我还看到了许多内存泄漏实例。根据我读过的一些消息来源,这可能是一个Qt 错误,但我想也许我可以通过销毁QPainter 对象来摆脱它,就像文档所说的那样->“记得销毁@ 987654327@ 绘制后的对象。”

这样做的正确方法是什么?

这是一个 valgrind 日志实例,其中drawText() 导致泄漏:

127971 ==00:00:05:31.916 24132== 68,594 (768 direct, 67,826 indirect) bytes in 2 blocks     are definitely lost in loss record 4,979 of 4,982
127972 ==00:00:05:31.916 24132==    at 0x4C2683D: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
127973 ==00:00:05:31.916 24132==    by 0x81861FF: ft_mem_qalloc (in /usr/lib64/libfreetype.so.6.6.2)
127974 ==00:00:05:31.916 24132==    by 0x8186242: ft_mem_alloc (in /usr/lib64/libfreetype.so.6.6.2)
127975 ==00:00:05:31.916 24132==    by 0x81876BE: FT_New_Library (in  /usr/lib64/libfreetype.so.6.6.2)
127976 ==00:00:05:31.916 24132==    by 0x81819C3: FT_Init_FreeType (in /usr/lib64/libfreetype.so.6.6.2)
127977 ==00:00:05:31.916 24132==    by 0x54E4667: ??? (in /usr/lib64/libQtGui.so.4.7.1)
127978 ==00:00:05:31.916 24132==    by 0x54E4A64:  QFontEngineFT::init(QFontEngine::FaceId, bool, QFontEngineFT::GlyphFormat) (in  /usr/lib64/libQtGui.so.4.7.1)
127979 ==00:00:05:31.916 24132==    by 0x54DE0B5: QFontEngineX11FT::QFontEngineX11FT(_FcPattern*, QFontDef const&, int) (in /usr/lib64/libQtGui.so.4.7.1)
127980 ==00:00:05:31.916 24132==    by 0x542EE80: ??? (in /usr/lib64/libQtGui.so.4.7.1)
127981 ==00:00:05:31.916 24132==    by 0x543721A: QFontDatabase::load(QFontPrivate const*, int) (in /usr/lib64/libQtGui.so.4.7.1)
127982 ==00:00:05:31.916 24132==    by 0x5414D46: QFontPrivate::engineForScript(int) const (in /usr/lib64/libQtGui.so.4.7.1)
127983 ==00:00:05:31.916 24132==    by 0x5429FDD: QFontMetricsF::leading() const (in /usr/lib64/libQtGui.so.4.7.1)
127984 ==00:00:05:31.916 24132==    by 0x534ED45: ??? (in /usr/lib64/libQtGui.so.4.7.1)
127985 ==00:00:05:31.916 24132==    by 0x534FCBD: QPainter::drawText(QRect const&, int, QString const&, QRect*) (in /usr/lib64/libQtGui.so.4.7.1)
127986 ==00:00:05:31.916 24132==    by 0x5863AC: gui::base::Printer::printTitle() (in /home/bed/workspace/tasks/MemProfile/MemoryProfiling/build-pc-debug/src/application-ui)
127987 ==00:00:05:31.916 24132==    by 0x585559: gui::base::Printer::run() (in /home/bed/workspace/tasks/MemProfile/MemoryProfiling/build-pc-debug/src/application-ui)
127988 ==00:00:05:31.916 24132==    by 0x5D49A5D: ??? (in /usr/lib64/libQtCore.so.4.7.1)
127989 ==00:00:05:31.916 24132==    by 0x7620A3E: start_thread (in /lib64/libpthread- 2.11.3.so)
127990 ==00:00:05:31.916 24132==    by 0x738067C: clone (in /lib64/libc-2.11.3.so)

【问题讨论】:

你现在如何使用 QPainter 对象?如果你创建一个如the documentation所示的局部变量,你不需要做任何事情来销毁它,因为它会在块的末尾被销毁。 我明白了......你认为是什么导致了 drawText() 上的内存泄漏? 如果您使用的是 QT 4.7.4 或 4.8,请查看此错误报告:bugreports.qt-project.org/browse/… 顺便说一句,如果 QPainter 在堆栈上创建并且您一遍又一遍地重用 QPainter(例如在游戏循环中),很明显 QPainter 本身保留了旧缓冲区。这与您的问题不是 100% 相关,但很明显 QPainter 确实存在错误。 【参考方案1】:

如果您在堆栈上创建 QPainter 对象(即不使用new),如文档中所述:

void SimpleExampleWidget::paintEvent(QPaintEvent *)

    QPainter painter(this);
    painter.setPen(Qt::blue);
    painter.setFont(QFont("Arial", 30));
    painter.drawText(rect(), Qt::AlignCenter, "Qt");

那么QPainter对象会在超出范围时被销毁(在上面的例子中:在函数的末尾)。文档中提到需要小心删除 QPainter 对象的原因是因为 Qt 本身不会这样做,这与很多 Qt 小部件管理有点不同。

因此,假设您改为动态分配此对象:

void SimpleExampleWidget::paintEvent(QPaintEvent *)

    QPainter* painter = new QPainter(this);
    painter->setPen(Qt::blue);
    painter->setFont(QFont("Arial", 30));
    painter->drawText(rect(), Qt::AlignCenter, "Qt");
    // Delete object, since Qt wont do it for you:
    delete painter;

Qt 告诉您您负责清理 QPainter 对象,在我们的示例中,这是使用 delete 命令完成的。但是,如果您使用这种方法,您可能会考虑将其放入智能指针中,即:boost::scoped_ptr<QPainter> painter = new QPainter(this);

void SimpleExampleWidget::paintEvent(QPaintEvent *)

    boost::scoped_ptr<QPainter> painter = new QPainter(this);
    painter->setPen(Qt::blue);
    painter->setFont(QFont("Arial", 30));
    painter->drawText(rect(), Qt::AlignCenter, "Qt");

现在,至于正确的方法。这取决于,如果您在本地创建 QPainter 对象,您需要多久创建一次,就像在示例中一样?如果这会导致开销,请考虑将 QPainter 创建为成员变量并重用它。 (不过,如果您担心速度:“尽早优化绝不是一个好主意”是应该牢记在心的)。

如果您使用new 动态分配内存,请记住您也负责删除它。如果你只是像最上面的例子一样在堆栈上创建它,你不必担心,因为它会在它退出创建它的范围时被销毁。

【讨论】:

我明白了......你认为是什么导致了 drawText() 上的内存泄漏? 你能把代码贴在你使用它的地方吗?以及来自 valgrind 的相应 memcheck 报告? 字体引擎初始化各种“静态”数据(不是真正的静态,而是为进程初始化一次),技术上泄露。因此 valgrind 警告了很多关于字体的东西。不过应该是无害的(因为,后续的 drawText() 调用不应导致进一步的泄漏。

以上是关于如何销毁 QPainter 对象/摆脱 drawText() 内存泄漏?的主要内容,如果未能解决你的问题,请参考以下文章

QPainter如何传输? (从工厂函数中移动一个对象)

QT使用painter绘制文字时的居中显示

如何使用 QPainter 缩放文本以适合边界框?

油漆事件外的QPainter画家对象

如何摆脱空的 StyleProtoChain 对象引用?

QT源码拾贝0-5(qimage和qpainter)