【问题标题】:Memory leak when using QThread and QPrinter使用 QThread 和 QPrinter 时的内存泄漏
【发布时间】:2017-05-18 14:30:33
【问题描述】:

我正在使用QThread 通过QPrinter 进行打印工作

我的PrintWorker 看起来像这样:

class PrintWorker : public QObject {
    Q_OBJECT

public:
    PrintWorker(QThread*, QPrinter*, QPicture*, QPainter*, QObject *parent = 0);

private:
    QPicture *_picture = nullptr;
    QPrinter *_printer = nullptr;
    QPainter *_painter = nullptr;

    public slots:
    void print();

signals:
    void done();
};

PrintWorker::PrintWorker(QThread *thread, QPrinter *printer, QPicture *picture, QPainter *painter, QObject *parent) :QObject(parent),
_picture(picture), _printer(printer), _painter(painter)
{
    moveToThread(thread);
    QObject::connect(thread, &QThread::started, this, &PrintWorker::print);
    QObject::connect(this, &PrintWorker::done, thread, &QThread::quit);
    QObject::connect(this, &PrintWorker::done, this, &PrintWorker::deleteLater);
    QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
}

void PrintWorker::print() {
    // do some print job with painter and picture
    emit done();
}

print 方法是这样的:

void NewService::print() {
    if (!_printer) { /* _printer : a private member */
        _printer = new QPrinter(QPrinter::HighResolution);
        _printer->setPageSize(QPrinter::A5);
        _printer->setPageOrientation(QPageLayout::Portrait);
        _printer->setColorMode(QPrinter::Color);
    }

    if (!_printDialog) { /* _printDialog : a private member */
        _printDialog = new QPrintDialog(_printer);
    }

    if (_printDialog->exec() == QPrintDialog::Accepted) {
        MyWidget *widget = new MyWidget(/* some args*/);

        QPainter *painter = new QPainter;
        QPicture *picture = new QPicture;
        widget->render(picture);

        QThread *thread = new QThread;
        PrintWorker *worker = new PrintWorker(thread, _printer, picture, painter);
        thread->start();
    }
}

现在在调用 print() 之前,我的应用在打印和调用 PrintWorker::print() 之后会暴露大约 9MB 的内存@我的应用的内存使用量达到 26MB

在另一个世界中,如果我们在 PrintWorker::print() 的最后部分删除 emit done,则没有任何区别。

我们期望在完成工作后内存使用量应该降至 26MB - 线程空间 + _printer + _printDialog 对象大小 ≈ 14MB

那么这有什么问题呢?

【问题讨论】:

  • 你有没有尝试添加一个 qDebug 看看你的 print worker 的析构函数是否被调用?
  • @rafaelgonzalez.Yes PrintWorker::~PrintWorker() 被调用。

标签: c++ qt memory-leaks qt5 qprinter


【解决方案1】:

您正在删除 PrintWorkerQThread 对象,但不是 QPainterQPictureMyWidgetQPrintDialogQPrinter。这些是内存泄漏(new 没有delete)。请注意,PrintWorker 析构函数可以负责删除QPainterQPictureQPrinter,此外,它还可以获取MyWidget 的所有权并将其删除。希望QPrintDialog 对象被NewService 类删除,但是由于代码没有发布,所以很难说。

此外,根据 rafael gonzalez 的建议,您应该检查 QThread PrintWorkerQThread 是否真的被删除了。

顺便问一下,你是如何检查内存使用情况的?通过泄漏检测工具(如 valgrind、VLD...)?或通过 Windows 任务管理器。因为任务管理器肯定是不准确的。

【讨论】:

  • 是的,测量工具是 Windows 任务管理器,是的,PrintWorker::~PrintWorker() 被调用,我不明白我将我的线程对象设为私有并设置 QObject::connect(_printThread, &QThread::destroyed, this, &NewService::onPrintDone); 现在我是通过qDebug() << _printThread; 看到QObject(0x2647bb040f0) !!所以它不会被删除
  • 删除_printThread不会改变指针地址,即使删除,它仍可能显示QObject(0x2647bb040f0)。此外,在QThreaddeleteLater 机制实际删除之前,可能会调用 onPrintDone。最后,任务管理器会向您显示应用程序的内存使用情况,当您删除对象时,内存使用情况不一定会缩小。您必须使用检漏仪工具。
【解决方案2】:

通过调用QThread::quit(),您将主动退出该线程中的 EventLoop。驻留在此线程中的对象的 QObject::deleteLater() 可能不再执行(尽管他们在 QThread::finished()-signal 中提到删除稍后的插槽仍将被调用)。

我个人只会使用finished() 信号建立连接,或者将worker-object的父级设置为线程本身,然后Qt实际上必须删除该对象。

http://doc.qt.io/qt-5/qthread.html#exit

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 2013-10-11
    • 1970-01-01
    相关资源
    最近更新 更多