首页 > 解决方案 > Qt 4.8 endInsert/RemoveRows 导致内存泄漏?

问题描述

在此处输入图像描述最近我对我的 Qt 4.8 应用程序进行了一些压力测试。我见过使用 valgrind massif 工具,它会导致堆内存扩展......

使用那个工具,我发现这个内存是用这个堆栈跟踪分配的(这是另一个地块调用,所以值与屏幕截图上的不同):

->02.11% (1,133,952B) 0x221FD9EB: ??? (in /usr/lib/x86_64-linux-gnu/qt4/plugins/accessible/libqtaccessiblewidgets.so)
| ->02.11% (1,133,952B) 0x80ADE69: QAccessible::queryAccessibleInterface(QObject*) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.7)
|   ->01.18% (637,824B) 0x80B5156: ??? (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.7)
|   | ->01.18% (637,824B) 0x8BA0F6E: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.7)
|   |   ->01.18% (637,824B) 0x8BF1472: QAbstractItemModel::rowsInserted(QModelIndex const&, int, int) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.7)
|   |     ->01.18% (637,824B) 0x8B86510: QAbstractItemModel::endInsertRows() (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.7)
|   |       ->01.18% (637,824B) in 5 places, all below massif's threshold (1.00%)

扩展的原因是 endInsertRows 和 endRemoveRows 函数。我的 ModelView 实现如下所示:

void TrainScheduleModelView::addTrain(const model::object::Train &train)
{
    if (this->m_rows == TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE)
    {
        beginRemoveRows(QModelIndex(),
                        TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE - 1,
                        TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE - 1);
        endRemoveRows();
    }

    beginInsertRows(QModelIndex(), 0, 0);
    this->m_trains[this->m_head].second = train;
    this->m_trains[this->m_head].first = true;

    if (0 == this->m_head)
    {
        this->m_head = TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE - 1;
    }
    else
    {
        --(this->m_head);
    }

    if (this->m_rows < TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE)
    {
        ++(this->m_rows);
    }
    endInsertRows();
}

该模型有意基于 C 数组,每次添加新对象时,都会截断最后一个对象。

谁能告诉我有没有错误,或者我用错了?

标签: c++linuxqt

解决方案


我有两次尝试解决它:

  1. 首先,我试图消除可访问性模块 - 我希望没有必要。我必须使用./configure ... -no-accessibility...选项从源代码构建 Qt。此案已解决,但应用程序的 UI 严重受损。我无法输入密码,因为键盘被阻止。所以这个解决方案是不可接受的。
  2. 下一个解决方法是消除动态行插入。我改变了方法,而不是模拟行插入,我总是返回固定数量的行(显示空行,但里面没有数据)。当我将一些数据放入表中时,我会发出dataChanged所有行都受影响的信号(因为每一行都被移动了)。该解决方案使用 queryAccessibleInterface 函数消除,并且没有泄漏。所以代码看起来像这样:
void TrainScheduleModelView::addTrain(const model::object::Train &train)
{
    this->m_trains[this->m_head].second = train;
    this->m_trains[this->m_head].first = true;

    if (0 == this->m_head)
    {
        this->m_head = TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE - 1;
    }
    else
    {
        --(this->m_head);
    }

    if (this->m_rows < TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE)
    {
        ++(this->m_rows);
    }

    emit dataChanged(this->index(0, 0),
                     this->index(TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE-1,
                                 TrainScheduleModelView::COLUMNS_AMOUNT - 1));
}

此案并未彻底解决。我想在 Ubuntu 上移植 Qt 或应用程序的初始化代码可能存在问题。

更新 我找到了泄漏源。queryAccessibleInterface函数返回分配有新指针和代码松散引用它。

void QAbstractItemViewPrivate::_q_layoutChanged()
{
    doDelayedItemsLayout();
#ifndef QT_NO_ACCESSIBILITY
#ifdef Q_WS_X11
    Q_Q(QAbstractItemView);
    if (QAccessible::isActive()) {
        QAccessible::queryAccessibleInterface(q)->table2Interface()->modelReset();
        QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged);
    }
#endif
#endif
}

推荐阅读