2

我正在用 C++/Qt 开发下载服务器。我正面临内存增长的问题。在这里,我分享示例服务器应用程序来演示该问题。

当客户端连接时,它开始每秒发送 10Kb 数据块。当客户端断开连接时,套接字将被删除。

#include <QCoreApplication>
#include <QtNetwork>

class Client: public QObject
{
    Q_OBJECT
public:
    Client(QSslSocket *sock)
    {
        this->timer = new QTimer(this);
        this->timer->setInterval(1000);
        connect(this->timer, SIGNAL(timeout()), this, SLOT(sendData()));

        this->sock = sock;
        connect(sock, SIGNAL(disconnected()), this, SIGNAL(disconnected()));         
        connect(sock, SIGNAL(encrypted()), this->timer, SLOT(start()));
        connect(sock, SIGNAL(readyRead()), this, SLOT(readData()));
    }

    ~Client()
    {
        delete this->sock;
    }

    void start()
    {
        this->sock->startServerEncryption();
    }

signals:
    void disconnected();

private slots:
    void sendData()
    {             
        qDebug() << "sending data via socket: " << sock->socketDescriptor();

        if (this->sock->bytesToWrite())
            return;

        QByteArray ba(10*1024, '1');
        this->sock->write(ba);
    }

    void readData()
    {
        this->sock->readAll();
    }

private:
    QSslSocket *sock;
    QTimer *timer;
}; // Client


class Server: public QTcpServer
{
    Q_OBJECT
public:
    Server()
    {     
        this->totalConnected = 0;
        this->instanceCounter = 0;
    }

protected:
    virtual void incomingConnection(int d);

private:
    int totalConnected;
    int instanceCounter;

private slots:
    void handleClientDisconnected();
    void handleDestroyed();
}; // Server

void Server::incomingConnection(int d)
{    
    QSslSocket *sock = new QSslSocket(this);

    if (!sock->setSocketDescriptor(d))
    {
        delete sock;
        return;
    }

    ++this->instanceCounter;

    qDebug() << "socket " << d << "connected, total: " << ++this->totalConnected;

    sock->setLocalCertificate(":/ssl/resources/my.crt");
    sock->setPrivateKey(":/ssl/resources/my.key", QSsl::Rsa, QSsl::Pem, "my.pass");

    Client *client = new Client(sock);
    connect(client, SIGNAL(disconnected()), this, SLOT(handleClientDisconnected()));
    connect(client, SIGNAL(destroyed()), this, SLOT(handleDestroyed()));

    client->start();
}

void Server::handleClientDisconnected()
{
    qDebug() << "client disconnected, total: " << --this->totalConnected;
    sender()->deleteLater();
}

void Server::handleDestroyed()
{
    qDebug() << "destroyed: " << --this->instanceCounter;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Server server;
    if (server.listen(QHostAddress::Any, 563))
        qDebug() << "listen started";
    else qDebug() << "listen failed";

    return a.exec();
}

#include "main.moc"

关于它有两个问题:

1)为什么下载时内存不断增长?

我正在使用 200-300 个连接进行测试。它在几分钟内达到 400 Mb,并且不会停止。

在 Client::sendData 我检查 this->sock->bytesToWrite() 以了解是否有等待写入的内容。因此,在所有内容都写入之前,永远不会添加新数据。所有数据块的大小都相同,因此它不可能为新数据分配更多内存。

2)为什么关闭所有连接时不返回所有内存?

尽管客户端断开连接时下载时使用的内存会下降,但看起来并没有全部返回。经过几次建立 200-700 个连接的测试,它达到了 80 Mb,并且在根本没有客户端的情况下一直保持在那个水平(客户端对象都被报告被破坏,实例计数器变为零)。

我正在阅读有关对象删除和内存释放之间的区别。据我了解,系统可能会将其保留以备将来需要(某种优化)。但是当其他东西需要它时,它肯定必须返回该内存。我决定写一个小程序,分配大量内存(即数组),看看是否会让系统赎回服务器使用的内存。它没有。该程序崩溃,因为没有足够的内存(服务器刚启动时它工作正常)。

所以看起来有什么问题。我怀疑泄漏,但内存泄漏检测器似乎没有发现任何严重的问题。Visual Leak Detector 报告没有内存泄漏。Valgrind 报告了一些问题,但它们指向 Qt 库,而我正在阅读它们只是误报,通常只是低级库在 valgrind 之后释放内存的副作用。无论如何,据报道丢失数据的总大小与 80 Mb 相比非常小。

4

1 回答 1

0

内存分配器可以配置为将未使用的内存块返回给系统,这是真的,但它们不会或实际上无法做到。

首先,您必须查看您的特定内存分配器,看看它是否配置为将内存返回给系统。这取决于您的操作系统和编译器,这两者都不是您提供的信息,但是这个 SO 问题应该为您回答:

malloc 实现会将释放的内存返回给系统吗?

它是否能够取决于堆的碎片。只有完整的内存块可以返回给系统,因此分散在整个堆中的微小分配将阻止这种情况(尽管分配器通常会尝试避免这种情况)。

于 2015-04-25T12:26:45.213 回答