Увеличение памяти сервера
Я занимаюсь разработкой сервера загрузок на C++/Qt. Я сталкиваюсь с проблемой роста памяти. Здесь я делюсь примером серверного приложения, чтобы продемонстрировать проблему.
Когда клиент подключен, он начинает отправлять фрагменты данных по 10 КБ каждую секунду. Когда клиент отключен, сокет удаляется.
#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 Мб за несколько минут и не собиралась останавливаться.
В Client::sendData я проверяю this->sock->bytesToWrite(), чтобы узнать, есть ли что-то, ожидающее записи. Таким образом, новые данные никогда не добавляются, пока все не будет записано. Все порции данных имеют одинаковый размер, поэтому не может быть необходимости выделять больше памяти для нового фрагмента данных.
2) Почему не вся память возвращается, когда все соединения закрыты?
Хотя объем памяти, используемой при загрузке, падает, когда клиенты отключены, похоже, что не все возвращается. После нескольких тестов установления 200-700 соединений он достиг 80 МБ и постоянно остается на том уровне, когда клиентов вообще нет (все объекты клиентов, как сообщается, уничтожаются, счетчик экземпляров обнуляется).
Я читал о разнице между удалением объекта и освобождением памяти. Насколько я понимаю, система может зарезервировать ее для будущих нужд (какая-то оптимизация). Но это, безусловно, должно вернуть эту память, когда что-то еще нужно. Я решил написать небольшую программу, которая выделяет большой объем памяти (а именно, массив), чтобы посмотреть, заставит ли система выкупать память, используемую сервером. Это не так. Эта программа дает сбой, потому что не хватает памяти (она отлично работает, когда сервер только запущен).
Похоже, что-то не так. Я подозревал утечку, но детекторы утечки памяти, похоже, не заметили каких-либо серьезных проблем. Визуальный детектор утечек сообщил, что не было утечек памяти. И Valgrind сообщил о некоторых проблемах, но они указывали на библиотеки Qt, и я читал, что это просто ложные тревоги, как правило, просто побочный эффект библиотек более низкого уровня, освобождающих память после valgrind. В любом случае, общий объем потерянных данных, по сообщениям, очень мал по сравнению с 80 Мб.
1 ответ
Распределители памяти могут быть сконфигурированы так, чтобы возвращать блоки неиспользуемой памяти в систему, правда, но это не так или физически не может.
Сначала вы должны посмотреть на свой конкретный распределитель памяти и посмотреть, настроен ли он для возврата памяти в систему. Это зависит от вашей ОС и вашего компилятора, ни одна из которых не является информацией, которую вы предоставляете, но этот вопрос SO должен ответить на этот вопрос для вас:
Вернут ли реализации malloc свободную память обратно в систему?
Будет ли это возможно, зависит от фрагментации кучи. Только полные блоки памяти могут быть возвращены в систему, поэтому крошечные выделения, распределенные по куче, предотвратят это (хотя распределители обычно пытаются этого избежать).