我正在用 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 相比非常小。