Qt 4.8 endInsert/RemoveRows 导致内存泄漏?
Posted
技术标签:
【中文标题】Qt 4.8 endInsert/RemoveRows 导致内存泄漏?【英文标题】:Qt 4.8 endInsert/RemoveRows cause memory leak? 【发布时间】:2020-04-01 13:43:38 【问题描述】:最近我对我的 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 数组,每次添加新对象时,都会截断最后一个。
谁能告诉我是有错误,还是我用错了?
【问题讨论】:
直接从文档中提取。关于beginRemoveRows
'...父索引对应删除新行的父索引; first 和 last 是要移除的行的行号....' 并与QModelIndex::QModelIndex()
'...这种类型的模型索引用于指示模型中的位置无效...' 所以您正在从无效的 QModelIndex 中删除行,而不是从正确的父级中删除行...有什么提示吗?
@JTejedor 我想你可能是对的,我明天会检查它:)
@JTejedor 我忘了说我基于 QAbstractTableModel,所以它不是树模型结构。但是,关于您的提示,我应该像QModelIndex parent = this->parent(this->index(row, 0));
这样提取父索引吗?我确信它将访问函数this->index
的第三个默认参数,这是无效的QModelIndex()
@JTejedor 不幸的是没有区别,我添加了以下几行:QModelIndex idx = this->index(TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE - 1, 0); QModelIndex parent = idx.parent(); beginRemoveRows(parent, TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE - 1, TrainScheduleModelView::MAX_TRAIN_SCHEDULE_SIZE - 1); endRemoveRows();
,插入之前是一样的,但我看不出有什么区别。
【参考方案1】:
我有两次尝试解决它:
-
首先我试图消除可访问性模块 - 我希望没有必要。我必须使用
./configure ... -no-accessibility...
选项从源代码构建 Qt。此案已解决,但应用程序的 UI 严重受损。我无法输入密码,因为键盘被阻止。所以这个解决方案是不可接受的。
下一个解决方法是取消动态行插入。我改变了方法,而不是模拟行插入,我总是返回固定数量的行(显示空行,但里面没有数据)。当我将一些数据放入表中时,我会发出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
【讨论】:
以上是关于Qt 4.8 endInsert/RemoveRows 导致内存泄漏?的主要内容,如果未能解决你的问题,请参考以下文章
Qt 4.8,Visual Studio 2013 编译错误
在 moveToThread() 之后未调用 Qt 4.8 信号/插槽
Qt 4.8:显示窗口时选择 QPushButton 上的文本