You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt Remote Objects高内存占用问题排查与优化咨询

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);
};

内存异常原因分析

  1. Qt RO属性同步的额外开销
    调用setByteArray时,Qt Remote Objects会对QByteArray进行序列化(通过QDataStream),序列化过程会生成临时缓存数据;同时RO源对象会缓存属性的旧值和新值,直到同步到客户端完成,两个过程叠加导致内存峰值远大于原始数组大小。

  2. QByteArray的内存分配与碎片
    代码通过循环append构建大数组,QByteArray会多次自动扩容,每次扩容都会预留额外空间,产生内存碎片;加上RO内部会复制这份数组数据,进一步增加内存占用。

  3. Qt内存池的延迟回收
    Qt内部维护内存池,大对象销毁后内存不会立即释放回操作系统,而是被内存池保留供后续分配使用,所以操作完成后内存不会回到初始值,这属于正常机制,并非内存泄漏,但会持续占用系统内存。

优化方案

  • 拆分大数据传输
    避免一次性同步超大QByteArray,将数据拆分为多个小数据包,分批次通过信号槽或属性同步传递,降低单次内存峰值。

  • 预分配内存减少扩容
    构建大QByteArray前,调用byteArray.reserve(65535 * 1000);预分配足够内存,减少自动扩容带来的内存碎片和临时内存开销。

  • 替换属性同步为信号传递
    如果不需要实时同步属性状态,改用REMOTESIGNAL定义远程信号,仅在需要时发送数据,避免RO属性同步带来的缓存和序列化额外开销。

  • 主动清理内存
    大内存操作完成后,调用byteArray.clear()释放数组内存;Linux环境下可调用malloc_trim(0)强制将内存释放回系统(Windows下用HeapCompact),注意这会影响内存分配性能,按需使用。

  • 减少RO内部缓存
    若频繁更新大属性,可参考Qt RO文档中关于属性缓存的配置,在每次设置新值后手动触发同步并清理旧值。

内容的提问来源于stack exchange,提问作者lin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 19:20:54