PDA

View Full Version : QSslSocket memory leak



mentalmushroom
8th October 2014, 07:59
Hello. I am developing the download server that should handle 1000+ simultaneous connections. I noticed some memory issue when there are many connections (well, perhaps, it happens with a small number of connections either, but it's just not that obvious). So I see not all the memory that was allocated while establishing connections is released when they are closed.

Here goes the simple server code. It doesn't send or receive any data.


#include <QCoreApplication>
#include <QtNetwork>

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

protected:
virtual void incomingConnection(int d);

private:
int total;

private slots:
void handleClientDisconnected();
};

void Server::incomingConnection(int d)
{
QSslSocket *sock = new QSslSocket;
if (!sock->setSocketDescriptor(d))
delete sock;

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

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

connect(sock, SIGNAL(disconnected()), this, SLOT(handleClientDisconnected()));

sock->startServerEncryption();
}

void Server::handleClientDisconnected()
{
QSslSocket *sock = static_cast<QSslSocket*>(sender());
qDebug() << "Client " << sock->socketDescriptor() << " disconnected, total: " << --this->total;
sock->deleteLater(); // ! deleting the disconnected socket
}

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"


This is how client connections are opened:


QSslSocket* sock[1200];

for (int i = 0; i < 500; ++i)
{
sock[i] = new QSslSocket;
sock[i]->connectToHostEncrypted("mytestserver.com", 563);
}


Again, now data is sent or received. I was running the test several times establishing and closing 200-500 connections. Now there is around 60 Mb of memory used by the server, although all connections were closed.

What is wrong in my server code, so it leaks much? The leak is bigger when transferring some data.

yeye_olive
8th October 2014, 11:41
Firstly, how do you know your server leaks memory? Just looking at the process' memory usage is a poor indicator, as the C++ runtime manages its own allocation. There is a huge difference between a delete and the deallocation of memory pages. Have you tried running your server through valgrind?

Secondly, your current code looks fragile to me. You only delete a socket upon receving its disconnected() signal. Are you certain you always receive it? What happens if you destroy the Server object first? Most likely, the connection from the sockets' disconnected() signal to the server's handleClientDisconnected() will be removed, and your sockets will leak. You could make the Server object the parent of the socket with QSslSocket *sock = new QSslSocket(this); or use QTcpServer::nextPendingConnection() which does that for you.

By the way, does your client delete its 500 sockets?

As a general rule, you should prevent resources from leaking by registering them with a manager. For QObject and derived classes, you can provide a parent upon creation. For other heap-allocated objects, the standard C++ library has a few useful smart pointer classes. This should be a real discipline, especially when exceptions and callbacks into user code lie around. Look up the RAII design pattern for more details.

mentalmushroom
8th October 2014, 11:58
Firstly, how do you know your server leaks memory? Just looking at the process' memory usage is a poor indicator, as the C++ runtime manages its own allocation.

I use top command to check the process memory. And I see it has grown to 60 Mb after closing all the connections.


There is a huge difference between a delete and the deallocation of memory pages.
Yes, I was reading about that, but I need somehow to deallocate that memory as it grows much when many clients are downloading.



Secondly, your current code looks fragile to me. You only delete a socket upon receving its disconnected() signal. Are you certain you always receive it? What happens if you destroy the Server object first? Most likely, the connection from the sockets' disconnected() signal to the server's handleClientDisconnected() will be removed, and your sockets will leak.

Well, this is a test sample, so I don't really care of what happens when the server object is destroyed first. I see memory growing without destroying it. And yes, I am sure I receive disconnected() signal as I was checking it with the counter, so when it turns to zero memory is not completely returned.



By the way, does your client delete its 500 sockets?

Does it matter? I am developing the server, not the client. And I want to fix the server issue regardless of the way the client works.



As a general rule, you should prevent resources from leaking by registering them with a manager. For QObject and derived classes, you can provide a parent upon creation. For other heap-allocated objects, the standard C++ library has a few useful smart pointer classes. This should be a real discipline, especially when exceptions and callbacks into user code lie around. Look up the RAII design pattern for more details.

I will take it into account, but it seems to me every socket gets deleted, but the memory is not always or not completely returned after that.

wysota
8th October 2014, 15:08
Yes, I was reading about that, but I need somehow to deallocate that memory as it grows much when many clients are downloading.

How much memory do you have that you are worried your kernel wants to keep those 60 megabytes allocated for you in case you will want them back in a couple of seconds? Did you see what happens when your system runs out of free memory? That is when it should take the memory away from you and not when you want it to. It knows better how expensive it is to allocate or deallocate memory to a process.

If you claim your application leaks memory then use tools for checking memory leaks and not tools reporting memory mapping.

mentalmushroom
8th October 2014, 15:37
How much memory do you have that you are worried your kernel wants to keep those 60 megabytes allocated for you in case you will want them back in a couple of seconds?
Well, 60 Mb this is what I got after few test runs. If I continue it will grow more. It will grow much more when there are 1000+ clients downloading. By the way, when there was much data sent to the socket it looks like the memory used for buffering is also not returned.


Did you see what happens when your system runs out of free memory?
Honestly, no, but in some cases it reached 1.5 gb (reported by top) and it doesn't really drop even when all clients are disconnected. However, I noticed memory may not grow, e.g. when I try to establish less connections then there were established before. So could it be the memory is not deallocated on purpose just to save future allocation time or something like that?

wysota
8th October 2014, 18:10
Well, 60 Mb this is what I got after few test runs.

I asked how much RAM you had in your computer, not how much you saw allocated to your process.


If I continue it will grow more. It will grow much more when there are 1000+ clients downloading.
Seems quite obvious that more objects means more memory usage.


By the way, when there was much data sent to the socket it looks like the memory used for buffering is also not returned.
You seem to miss the point. The fact that you call "free" or "delete" on your data doesn't mean the system will deallocate memory from your process. If you use much memory in a short period of time the system will notice that and will keep the memory allocated to your process in case yo want to use more memory again.


Honestly, no, but in some cases it reached 1.5 gb (reported by top) and it doesn't really drop even when all clients are disconnected.
Launch another program and have it allocate almost all of your physical memory and do something with all the memory (e.g. fill it with value '42'). Then see if amount of memory allocated to your server drops. It will because the system will reclaim memory from your program in order to give it to the other one.


So could it be the memory is not deallocated on purpose just to save future allocation time or something like that?
Congratulations, now read our posts again.

mentalmushroom
9th October 2014, 14:27
I asked how much RAM you had in your computer, not how much you saw allocated to your process.
On the test server there is 512 Mb. I thought you mean why am I so worried about 60 Mb. I just tried to say memory grows significantly if I test it well, so this is why I care about it.


Seems quite obvious that more objects means more memory usage.
Yes, but I mean when all those objects (namely, QSslSocket's) are destroyed, memory stays high. Even after few days it stays at 1.5 gb and doesn't go down.


Launch another program and have it allocate almost all of your physical memory and do something with all the memory (e.g. fill it with value '42'). Then see if amount of memory allocated to your server drops. It will because the system will reclaim memory from your program in order to give it to the other one.
Ok, tried that. It proved to be partially correct, as it looked like memory was deallocated with some delay. But still if I run a fresh instance of the server, I can run another program requiring 400 Mb of memory. If I use the server a little and close all client connections, wait and then run that other app, it will fail. So I don't know what I am missing. I was trying Visual Leak Detector, it reports 44 bytes leak:



Call Stack:
z:\testserver-ssl\main.cpp (17): testserver-ssl.exe!Server::Server + 0x7 bytes
z:\testserver-ssl\main.cpp (192): testserver-ssl.exe!main + 0x8 bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (586): testserver-ssl.exe!__tmainCRTStartup + 0x19 bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (403): testserver-ssl.exe!mainCRTStartup
0x7656919F (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0xE bytes
0x7703A22B (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x84 bytes
0x7703A201 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x5A bytes


Strange, but not that much to care about.

Perhaps, you could suggest me some another leak detector as VLD seems to be Windows-only and I am developing the server that should run on Ubuntu Server.

wysota
9th October 2014, 21:43
On the test server there is 512 Mb.
Hmm... You want to handle 1k simultaneous clients with hardware that has 512MB of physical memory? That is going to be quite hard.



Perhaps, you could suggest me some another leak detector as VLD seems to be Windows-only and I am developing the server that should run on Ubuntu Server.

Valgrind

mentalmushroom
10th October 2014, 06:40
Hmm... You want to handle 1k simultaneous clients with hardware that has 512MB of physical memory? That is going to be quite hard.
No-no, the server where the real application will run has 4 Gb RAM and we'll add more if needed. But there are two 'small' servers that I use for basic testing, researching etc. By the way, what amount of memory in your opinion the download server should have to handle several thousands of connections?


Valgrind
Ok, thanks. Although appears to be difficult to understand for me.

wysota
10th October 2014, 07:06
valgrind --tool=memcheck --leak-check=full yourappname yourappargs

mentalmushroom
10th October 2014, 08:01
Ok, this is what valgrind reports. I see some possible errors, but they all seem to point to Qt libs. Also the possible leak is ~14 Kb only, but I see the program takes much more. Could it be that QTcpServer, for example, takes much memory when there are many connections to it? As this object is not deleted when clients are disconnected.

==10703== HEAP SUMMARY:
==10703== in use at exit: 299,851 bytes in 4,365 blocks
==10703== total heap usage: 1,489,448 allocs, 1,485,083 frees, 4,536,782,939 bytes allocated
==10703==
==10703== 1,440 bytes in 6 blocks are possibly lost in loss record 896 of 940
==10703== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C6A8D: g_slist_prepend (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AB2A0: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x5316189: QSocketNotifier::QSocketNotifier(int, QSocketNotifier::Type, QObject*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F084D7: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x4EFBB7D: QAbstractSocket::setSocketDescriptor(int, QAbstractSocket::SocketState, QFlags<QIODevice::OpenModeFlag>) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x4F1E7F3: QSslSocket::setSocketDescriptor(int, QAbstractSocket::SocketState, QFlags<QIODevice::OpenModeFlag>) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x402339: Server::incomingConnection(int) (main.cpp:37)
==10703==
==10703== 1,488 bytes in 3 blocks are possibly lost in loss record 900 of 940
==10703== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AA245: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AB2B8: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x5316189: QSocketNotifier::QSocketNotifier(int, QSocketNotifier::Type, QObject*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F084D7: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x4EFBB7D: QAbstractSocket::setSocketDescriptor(int, QAbstractSocket::SocketState, QFlags<QIODevice::OpenModeFlag>) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x4F1E7F3: QSslSocket::setSocketDescriptor(int, QAbstractSocket::SocketState, QFlags<QIODevice::OpenModeFlag>) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x402339: Server::incomingConnection(int) (main.cpp:37)
==10703==
==10703== 1,680 bytes in 7 blocks are possibly lost in loss record 905 of 940
==10703== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C6A8D: g_slist_prepend (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AB2A0: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F0093B: QAbstractSocket::writeData(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x528BCAE: QIODevice::write(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F24C6E: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x4F1EF88: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x530D280: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703==
==10703== 1,920 bytes in 8 blocks are possibly lost in loss record 907 of 940
==10703== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C6A8D: g_slist_prepend (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AB2A0: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x5316189: QSocketNotifier::QSocketNotifier(int, QSocketNotifier::Type, QObject*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F085CA: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x4F0093B: QAbstractSocket::writeData(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x528BCAE: QIODevice::write(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F24C6E: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703==
==10703== 2,480 bytes in 5 blocks are possibly lost in loss record 912 of 940
==10703== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AA245: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AB2B8: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x5316189: QSocketNotifier::QSocketNotifier(int, QSocketNotifier::Type, QObject*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F085CA: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x4F0093B: QAbstractSocket::writeData(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x528BCAE: QIODevice::write(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F24C6E: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703==
==10703== 2,480 bytes in 5 blocks are possibly lost in loss record 913 of 940
==10703== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10703== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AA245: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x67AB2B8: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==10703== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F0093B: QAbstractSocket::writeData(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x528BCAE: QIODevice::write(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703== by 0x4F24C6E: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x4F1EF88: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==10703== by 0x530D280: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==10703==
==10703== LEAK SUMMARY:
==10703== definitely lost: 0 bytes in 0 blocks
==10703== indirectly lost: 0 bytes in 0 blocks
==10703== possibly lost: 14,064 bytes in 45 blocks
==10703== still reachable: 285,787 bytes in 4,320 blocks
==10703== suppressed: 0 bytes in 0 blocks
==10703== Reachable blocks (those to which a pointer was found) are not shown.
==10703== To see them, rerun with: --leak-check=full --show-reachable=yes
==10703==
==10703== For counts of detected and suppressed errors, rerun with: -v
==10703== Use --track-origins=yes to see where uninitialised values come from
==10703== ERROR SUMMARY: 5389 errors from 14 contexts (suppressed: 2 from 2)

yeye_olive
10th October 2014, 15:06
IIRC some valgrind configurations exist for popular libraries like Qt that filter some of the noise caused by said libraries from valgrind's output.

You mentioned an increase in memory usage when clients start downloading, and you suspect that this memory is never freed. There could indeed be a leak in the code reading from/writing to sockets in your server, which we have yet to see.

mentalmushroom
13th October 2014, 08:00
This is the sample code for the server, that sends data:



#include <iostream>

#include <QCoreApplication>
#include <QtNetwork>

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

this->sock = sock;
connect(sock, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
//connect(sock, SIGNAL(disconnected()), this, SLOT(deleteLater()));
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;

char buf[10*1024];
memset(buf, 1, sizeof buf);
this->sock->write(buf, sizeof buf);
}

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;

if (this->instanceCounter == 0)
{
std::cout << "quit?" << std::endl;
char ch;
std::cin >> ch;
if (ch == 'y')
{
QCoreApplication::instance()->quit();
}
}
}

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"


There are two questions about it:
1) Why memory is constantly growing while downloading? It reached 350 Mb and was still growing, so I closed all client connections to prevent it from crashing. If you check the code, you'll see the data chunks are of the fixed size that is 10 Kb and I perform the check for bytesToWrite before sending new data, so there should be no new data sent to the socket when there is still something to write. So it's not clear how could it be that memory has grown so much.

2) Why not all the memory is returned when clients are disconnected? After two tests of establishing 500 connections and downloading for 1-2 minutes memory reached 50 Mb when all clients were disconnected and destroyed. If I do more tests or establish more connections this value will be higher.

And I am adding the valgrind report for this program (some repeating messages removed):


==11184==
==11184== HEAP SUMMARY:
==11184== in use at exit: 137,830 bytes in 3,218 blocks
==11184== total heap usage: 158,341 allocs, 155,123 frees, 374,941,034 bytes allocated
==11184==
...
==11184== 238 (96 direct, 142 indirect) bytes in 1 blocks are definitely lost in loss record 614 of 698
==11184== at 0x4C2B1C7: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11184== by 0x52E710E: ??? (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x52E73DA: QLibrary::setFileNameAndVersion(QString const&, QString const&) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x4F28737: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F2961B: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F2099B: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F22248: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F11CFE: QSslCertificate::QSslCertificate(QByteArray const&, QSsl::EncodingFormat) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F1ADF6: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F2231C: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F1F1AD: QSslSocket::QSslSocket(QObject*) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4026BB: Server::incomingConnection(int) (main.cpp:101)
....
==11184==
==11184== 242 (96 direct, 146 indirect) bytes in 1 blocks are definitely lost in loss record 619 of 698
==11184== at 0x4C2B1C7: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11184== by 0x52E710E: ??? (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x52E73DA: QLibrary::setFileNameAndVersion(QString const&, QString const&) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x52E744B: QLibrary::QLibrary(QString const&, QString const&, QObject*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x5253894: ??? (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x5219F10: QLocalePrivate::updateSystemPrivate() (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x521A1EA: ??? (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x521A21C: ??? (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x521A389: QLocale::QLocale() (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x52A5869: ??? (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x52CCEEE: ??? (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x52CD3AA: ??? (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184==
...
==11184==
==11184== 496 bytes in 1 blocks are possibly lost in loss record 637 of 698
==11184== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11184== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11184== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67AA245: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67AB2B8: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x5316189: QSocketNotifier::QSocketNotifier(int, QSocketNotifier::Type, QObject*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x4F084D7: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4EFBB7D: QAbstractSocket::setSocketDescriptor(int, QAbstractSocket::SocketState, QFlags<QIODevice::OpenModeFlag>) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F1E7F3: QSslSocket::setSocketDescriptor(int, QAbstractSocket::SocketState, QFlags<QIODevice::OpenModeFlag>) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4026EB: Server::incomingConnection(int) (main.cpp:103)
==11184==
==11184== 496 bytes in 1 blocks are possibly lost in loss record 638 of 698
==11184== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11184== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11184== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67AA245: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67AB2B8: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x4F0093B: QAbstractSocket::writeData(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x528BCAE: QIODevice::write(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x4F24C6E: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F1EF88: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x530D280: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
....
==11184== 960 bytes in 4 blocks are possibly lost in loss record 661 of 698
==11184== at 0x4C29BE8: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11184== by 0x4C29C97: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11184== by 0x677F0C9: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67C60C3: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67C6A8D: g_slist_prepend (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x67AB2A0: g_source_add_poll (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.3200.4)
==11184== by 0x53287A9: QEventDispatcherGlib::registerSocketNotifier(QSock etNotifier*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x5316189: QSocketNotifier::QSocketNotifier(int, QSocketNotifier::Type, QObject*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x4F085CA: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x4F0093B: QAbstractSocket::writeData(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
==11184== by 0x528BCAE: QIODevice::write(char const*, long long) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.1)
==11184== by 0x4F24C6E: ??? (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.1)
...
==11184==
==11184== LEAK SUMMARY:
==11184== definitely lost: 384 bytes in 4 blocks
==11184== indirectly lost: 580 bytes in 12 blocks
==11184== possibly lost: 7,312 bytes in 23 blocks
==11184== still reachable: 129,554 bytes in 3,179 blocks
==11184== suppressed: 0 bytes in 0 blocks
==11184== Reachable blocks (those to which a pointer was found) are not shown.
==11184== To see them, rerun with: --leak-check=full --show-reachable=yes
==11184==
==11184== For counts of detected and suppressed errors, rerun with: -v
==11184== Use --track-origins=yes to see where uninitialised values come from
==11184== ERROR SUMMARY: 1885 errors from 14 contexts (suppressed: 2 from 2)

wysota
13th October 2014, 08:52
Hmm... why are you using a timer to send data? What is the purpose of this code?


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

Nevertheless I don't see any obvious memory leaks here.

mentalmushroom
13th October 2014, 09:44
why are you using a timer to send data?
The timer was used to simulate periodic data sending, which in a real application happens as a result of client-server communication based on a network protocol. But this is just a test sample that I tried to make as simple as possible to show the issue.



What is the purpose of this code?

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



This code checks whether there is something waiting to be sent on the socket. Thus I was trying not to write anything while the socket is busy in a hope it will maybe prevent continuous memory growing during data sending. Unfortunately, it doesn't seem to help. Memory still grows and rather quickly.

wysota
13th October 2014, 11:41
There is no such thing as the socket being "busy". Sockets have buffers. And I don't see a leak in your Valgrind report. Earlier you said that memory use grows rather quickly, I don't see a "rather quickly" leak reports in what you posted. How exactly does "rather quickly" manifest itself? Are you checking using top again?

mentalmushroom
13th October 2014, 12:13
There is no such thing as the socket being "busy". Sockets have buffers.

Well, saying "busy" I meant when there is something waiting to be written. I was assuming memory was growing, because too much data was hold in the buffer. So I tried not to add new data there.



Earlier you said that memory use grows rather quickly, I don't see a "rather quickly" leak reports in what you posted. How exactly does "rather quickly" manifest itself?

I don't see it either, but it reaches 350 Mb in a few minutes.


Are you checking using top again?
Yes, I do. I remember you told me that memory may not be deallocated and it doesn't mean there is a leak, BUT it looks like I can't redeem it. For example, it reached 350 Mb when I was downloading with 500 connections. Then I closed all of them. I see all client objects destroyed. Memory dropped to 30 Mb. Then I do the same test again. Memory reaches ~300 Mb again. I close connections. It drops to 50 Mb. And so on. If I continue, memory will be 100 or 200 Mb even when all clients are disconnected. It stays on the same level permanently and nothing else can use that memory.

wysota
13th October 2014, 13:33
Yes, I do.
No comment.


It stays on the same level permanently and nothing else can use that memory.
How do you know that?

mentalmushroom
13th October 2014, 13:45
How do you know that?
Following your suggestion I created a program that allocates a large amount of memory (namely, that is a dynamic array). For example, it can allocate 400 Mb when the program is just started, but after several tests it can't do that anymore.


No comment.
Is there a leak or not, but I am worried that memory is not returned. It may grow to really high levels and other programs can't use it. Eventually the program, will crash, I think (could not reach that so far). Valgrind validation doesn't mean a thing, when there are such problems. This is why I use top.