Qt Remote Objects高内存占用问题排查与优化咨询
问题背景
近期学习Qt Remote Objects时,编写了一个客户端-服务器架构的Demo,遇到了内存异常波动的问题:
- 在
byteArrayTest函数中创建约63MB的QByteArray后,调用setByteArray同步属性,内存瞬间涨到302MB,之后回落至194MB - 当nCount=10时,设置一个仅1字节的QByteArray,内存仍维持在130MB左右
代码展示
服务器头文件(SimpleSwitch.h)
#ifndef SIMPLESWITCH_H #define SIMPLESWITCH_H #include <QByteArray> #include "rep_simpleswitch_source.h" class SimpleSwitch : public SimpleSwitchSimpleSource { Q_OBJECT public: SimpleSwitch(QObject* parent = nullptr); ~SimpleSwitch(); void byteArrayTest(char nChar); public Q_SLOTS: void timeout_slot(); private: QTimer* stateChangeTimer; }; #endif // SIMPLESWITCH_H
服务器源文件(SimpleSwitch.cpp)
#include "simpleswitch.h" #include <QTimer> #include <QSignalSpy> SimpleSwitch::SimpleSwitch(QObject* parent) : SimpleSwitchSimpleSource(parent) { stateChangeTimer = new QTimer(this); QObject::connect(stateChangeTimer, &QTimer::timeout, this, &SimpleSwitch::timeout_slot); stateChangeTimer->start(2000); } SimpleSwitch::~SimpleSwitch() { stateChangeTimer->stop(); } void SimpleSwitch::timeout_slot() { static int nCount = 1; if(nCount == 5) { qDebug() << "nCount = "<< nCount; byteArrayTest(1); } if (nCount == 10) { qDebug() << "nCount = " << nCount; QByteArray byteArray; byteArray.append(1); setByteArray(byteArray); //③ Memory has become 130MB stateChangeTimer->stop(); } ++nCount; } void SimpleSwitch::byteArrayTest(char nChar) { QByteArray byteArray; for(int index = 0; index < 65535 * 1000; ++index) { byteArray.append(nChar+index); } //① Allocated 63MB of memory // 65535 * 1000 / 1024 / 1024 = 63M setByteArray(byteArray); //② The memory has increased to 302MB //after ② Memory has returned to 194MB }
客户端代码(Client.h)
#ifndef CLIENT_H #define CLIENT_H #include <QObject> #include <QSharedPointer> #include "rep_simpleswitch_replica.h" class Client : public QObject { Q_OBJECT public: Client(QSharedPointer<SimpleSwitchReplica> ptr) : QObject(nullptr), reptr(ptr) { QObject::connect(reptr.data(), &SimpleSwitchReplica::byteArrayChanged, this, &Client::onByteArrayChanged); } ~Client() { } public Q_SLOTS: void onByteArrayChanged(QByteArray byteArray) { qDebug() << "onByteArrayChanged"; } private: QSharedPointer<SimpleSwitchReplica> reptr; }; #endif // CLIENT_H
定义文件(rep_simpleswitch.rep)
class SimpleSwitch { PROP(QByteArray byteArray); };
内存异常原因分析
Qt RO属性同步的额外开销
调用setByteArray时,Qt Remote Objects会对QByteArray进行序列化(通过QDataStream),序列化过程会生成临时缓存数据;同时RO源对象会缓存属性的旧值和新值,直到同步到客户端完成,两个过程叠加导致内存峰值远大于原始数组大小。QByteArray的内存分配与碎片
代码通过循环append构建大数组,QByteArray会多次自动扩容,每次扩容都会预留额外空间,产生内存碎片;加上RO内部会复制这份数组数据,进一步增加内存占用。Qt内存池的延迟回收
Qt内部维护内存池,大对象销毁后内存不会立即释放回操作系统,而是被内存池保留供后续分配使用,所以操作完成后内存不会回到初始值,这属于正常机制,并非内存泄漏,但会持续占用系统内存。
优化方案
拆分大数据传输
避免一次性同步超大QByteArray,将数据拆分为多个小数据包,分批次通过信号槽或属性同步传递,降低单次内存峰值。预分配内存减少扩容
构建大QByteArray前,调用byteArray.reserve(65535 * 1000);预分配足够内存,减少自动扩容带来的内存碎片和临时内存开销。替换属性同步为信号传递
如果不需要实时同步属性状态,改用REMOTESIGNAL定义远程信号,仅在需要时发送数据,避免RO属性同步带来的缓存和序列化额外开销。主动清理内存
大内存操作完成后,调用byteArray.clear()释放数组内存;Linux环境下可调用malloc_trim(0)强制将内存释放回系统(Windows下用HeapCompact),注意这会影响内存分配性能,按需使用。减少RO内部缓存
若频繁更新大属性,可参考Qt RO文档中关于属性缓存的配置,在每次设置新值后手动触发同步并清理旧值。
内容的提问来源于stack exchange,提问作者lin

