valgrind memory leak

Alles rund um die Programmierung mit Qt
Antworten
DBGTMaster
Beiträge: 190
Registriert: 19. August 2010 10:00

valgrind memory leak

Beitrag von DBGTMaster »

Hallo,

Ich habe nun mein Programm schon so klein wie möglich abgespeckt, aber ich erhalte pro gestarteten Thread 1Byte an Memory Leak... Ja klar, 1 byte ist nichts, aber wenn man das x1.000.000 rechnet, dann schon.

Folgende Situation:
Ich starte für jede eingehende Verbindung einen neuen Thread, welche diese Verbindung verwaltet. Valgrind sagt mir, dass beim erstellen des Threads ein Memory Leak entsteht. Ich weiß bloß nicht, warum :(...

Hier die minimierte Version:

main.cpp:

Code: Alles auswählen

#include <QtCore>
#include <TcpServer.h>

// The main function :) ...
int main(int argc, char **argv) {

    QCoreApplication app(argc, argv);

    TcpServer tcpServer;
    quint16 TCP_PORT = 4500;
    tcpServer.listen(QHostAddress::Any, TCP_PORT);

    // Nun lassen wir den Server seinen Spaß :)...
    return app.exec();
}
TcpServer.h:

Code: Alles auswählen

#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QtNetwork/QTcpServer>
#include <TcpSocketThread.h>

class TcpServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit TcpServer(QObject *parent = 0);

    // ... um Anfragen in einem eigenen Thrad zu starten:
    void incomingConnection(int handle);
signals:

public slots:

};

#endif // TCPSERVER_H
TcpServer.cpp:

Code: Alles auswählen

#include "TcpServer.h"

TcpServer::TcpServer(QObject *parent) :
    QTcpServer(parent)
{

}

// Methode wird aufgerufen, wenn eine neue Eingehende Verbindung
// verfügbar ist.
void TcpServer::incomingConnection(int handle) {

    // Neuen Thread erstellen...
    TcpSocketThread *thread = new TcpSocketThread;

    // Wenn Thread beendet wurde, so die Ressourcen des Threads freigeben...
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    // ... Und Action!
    thread->start();
}
TcpSocketThread.h:

Code: Alles auswählen

#ifndef TCPSOCKETTHREAD_H
#define TCPSOCKETTHREAD_H

#include <QObject>
#include <QThread>

class TcpSocketThread : public QThread
{
    Q_OBJECT

public:
    TcpSocketThread(QObject *parent = 0);

    // Die Threadausführung
    void run();


public slots:
};

#endif // TCPSOCKETTHREAD_H
TcpSocketThread.cpp:

Code: Alles auswählen

#include <TcpSocketThread.h>

#include <memory>

#include <QHostAddress>

#include <TcpCommands/main/userLogin.h>
#include <TcpCommands/main/serverVersion.h>

#include <Logger/Logger.h>

#include <TcpCommandFactories.h>

TcpSocketThread::TcpSocketThread(QObject *parent) : QThread(parent)
{

    // Sich selber in den Thread verschieben...
    moveToThread(this);

}

void TcpSocketThread::run() {

}
Starte ich das Programm, baue eine TCP- Verbindung auf Port 4500 auf, beende das Programm danach, sagt Valgrind:
==13714== Memcheck, a memory error detector
==13714== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==13714== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==13714== Command: ./intranet_server
==13714==
^C==13714==
==13714== HEAP SUMMARY:
==13714== in use at exit: 96,081 bytes in 339 blocks
==13714== total heap usage: 2,951 allocs, 2,612 frees, 265,357 bytes allocated
==13714==
==13714== 1,048 (16 direct, 1,032 indirect) bytes in 1 blocks are definitely lost in loss record 190 of 198
==13714== at 0x4C28B42: operator new(unsigned long) (vg_replace_malloc.c:261)
==13714== by 0x40168B: TcpServer::incomingConnection(int) (TcpServer.cpp:14)
==13714== by 0x5EDADFA: ??? (in /home/thomas/QtSDK/Desktop/Qt/474/gcc/lib/libQtNetwork.so.4.7.4)
==13714== by 0x5EC3680: ??? (in /home/thomas/QtSDK/Desktop/Qt/474/gcc/lib/libQtNetwork.so.4.7.4)
==13714== by 0x62E31EE: QCoreApplicationPrivate::notify_helper(QObject*, QEvent*) (in /home/thomas/QtSDK/Desktop/Qt/474/gcc/lib/libQtCore.so.4.7.4)
==13714== by 0x62E326D: QCoreApplication::notify(QObject*, QEvent*) (in /home/thomas/QtSDK/Desktop/Qt/474/gcc/lib/libQtCore.so.4.7.4)
==13714== by 0x62E2DC3: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /home/thomas/QtSDK/Desktop/Qt/474/gcc/lib/libQtCore.so.4.7.4)
==13714== by 0x6310889: ??? (in /home/thomas/QtSDK/Desktop/Qt/474/gcc/lib/libQtCore.so.4.7.4)
==13714== by 0x77CFBCC: g_main_context_dispatch (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.2800.6)
==13714== by 0x77D03A7: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.2800.6)
==13714== by 0x77D0638: g_main_context_iteration (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.2800.6)
==13714== by 0x6310A1B: QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/thomas/QtSDK/Desktop/Qt/474/gcc/lib/libQtCore.so.4.7.4)
==13714==
==13714== LEAK SUMMARY:
==13714== definitely lost: 16 bytes in 1 blocks
==13714== indirectly lost: 1,032 bytes in 12 blocks
==13714== possibly lost: 0 bytes in 0 blocks
==13714== still reachable: 95,033 bytes in 326 blocks
==13714== suppressed: 0 bytes in 0 blocks
==13714== Reachable blocks (those to which a pointer was found) are not shown.
==13714== To see them, rerun with: --leak-check=full --show-reachable=yes
==13714==
==13714== For counts of detected and suppressed errors, rerun with: -v
==13714== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
Baue ich 10 Verbindungen auf, habe ich den 10- fachen Verlust...

lG
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: valgrind memory leak

Beitrag von Christian81 »

Wie beendest Du das Programm? Sehe nicht dass hier was geordnet beendet wird...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
DBGTMaster
Beiträge: 190
Registriert: 19. August 2010 10:00

Re: valgrind memory leak

Beitrag von DBGTMaster »

Aber ganz genau das glaube ich nicht, dass es daran liegt:

valgrind:
: TcpServer::incomingConnection(int) (TcpServer.cpp:14)
Dieses Objekt sollte zum Zeitpunkt des beendens ja gar nicht mehr existieren...
DBGTMaster
Beiträge: 190
Registriert: 19. August 2010 10:00

Re: valgrind memory leak

Beitrag von DBGTMaster »

Habe die main() funktion nun so weit abgeändert, dass sich das Programm nach 15 Sekunden beendet, Memory leak bleibt:

Code: Alles auswählen

#include <QtCore>
#include <TcpServer.h>

// The main function :) ...
int main(int argc, char **argv) {

    QCoreApplication app(argc, argv);

    /**
      * Tcp- Server erstellen:
      */
    TcpServer tcpServer;
    quint16 TCP_PORT = 4500;
    tcpServer.listen(QHostAddress::Any, TCP_PORT);

    QTimer::singleShot(15000, &app, SLOT(quit()) );

    // Nun lassen wir den Server seinen Spaß :)...
    return app.exec();
}
Ich starte das Programm, baue eine telnet Verbindung auf, beende diese, und warte ab, bis sich das Programm ändert...

Valgrind gibt gleiche Ausgabe..

lG
DBGTMaster
Beiträge: 190
Registriert: 19. August 2010 10:00

Re: valgrind memory leak

Beitrag von DBGTMaster »

OK, ich hab den Fehler gefunden :)...

Ist liegt am moveToThread(this) im Konstruktor der Thread Klasse, entferne ich dieses, ist das Leak auch verschwunden...

Um das Problem zu lösen, habe ich den Slot deleteLater() der Thread Klasse überladen:

Code: Alles auswählen

void Thread::deleteLater() {

    moveToThread(QApplication::instance()->thread());
}
Nun funktionierts! Aber meine Frage, warum muss ich das Objekt zum Hauptthread verschieben?? Ist sonst das löschen des Objektes nicht möglich??
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Re: valgrind memory leak

Beitrag von solarix »

"deleteLater()" hinterlegt das entsprechende QObjekt im Eventloop des zugehörigen Threads als "Löschauftrag". Der Eventloop dieses Threads löscht dann das Objekt, sobald sinnvoll (sobald keine Events mehr für dieses Objekt anstehen).
Also: bei einem "moveToThread(this)" müsste/würde sich der Thread selbst löschen, weil er ja den Eventloop besitzt:

Code: Alles auswählen

.... run()
{
  exec(); // hier würde das "deleteLater()" ausgeführt..
}
Erstens ist das sowieso nicht sinnvoll (weil der Thread ja noch läuft) und zweitens hat du in deinem Beispiel kein Eventloop (run() ist leer), daher wird der Löschauftrag ja auch nie ausgeführt.

Das "moveToThread.." in "deleteLater()" auszuführen ist übrigens gleich auch quatsch, weil man es dann nicht mehr vom GUI-Kontext her aufrufen darf (moveToThread() darf ein Objekt nur "einem anderen Kontext schenken" (in der Doku "push" genannt) und nie "von einem anderen Kontext klauen" ("pull"). Das bedeutet es müsste wie folgt imeplementiert werden:

Code: Alles auswählen

void Thread::deleteLater() 
{
    Q_ASSERT(QThread::currentThread() == thread());    // Thread-Affinitaet gewaehrleisten ("pull" verhindern)
    moveToThread(QApplication::instance()->thread());  // vom Thread in die GUI schieben
    QThread::deleteLater();                                           // Loeschauftrag in der GUI hinterlegen
}
Zwei weitere Hinweise:
1. Qt macht Sockets bereits threaded.. für TCP/IP-Clients braucht man daher im Normalfall keinen Extra-Thread.
2. Um deine Probleme zu umgehen, empfehlen die Trolls ein anderes Thread-Konzept: http://labs.qt.nokia.com/2006/12/04/thr ... -headache/

hth!
DBGTMaster
Beiträge: 190
Registriert: 19. August 2010 10:00

Re: valgrind memory leak

Beitrag von DBGTMaster »

Ich erstelle mit Absicht einen eigenen Thread, abre das hat nun andere Gründe :)....

Und in meinen wirklichen Code hat der Thread auch ein Event-Loop...

Danke auf jeden Fall für deine Antwort, hat mal wieder weitergeholfen =)..

lG
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: valgrind memory leak

Beitrag von Christian81 »

Aber die Eventloop wird nicht korrekt beendet und demnach auch kein löschen - wie ich schon in meinem ersten Thread gesagt habe...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Antworten