Speicher wird nicht freigegeben

Alles rund um die Programmierung mit Qt
Antworten
marvel
Beiträge: 105
Registriert: 9. August 2009 07:41

Speicher wird nicht freigegeben

Beitrag von marvel »

hallo,
ich habe ein ziemlich ernsthaftes problem mit meinem programm. ich lege während der laufzeit ca 1 mio objekte auf dem heap an. diese objekte werden erzeugt, indem eine xml datei geparset wird und jedes tag als objekt angelegt wird. wenn ich den taskmanager öffne verbraucht mein programm fast 570000k speicher. soweit alles kein problem wenn man einen relativ neuen rechner hat. wenn ich aber nach allen berechnungen die objekte wieder löschen bzw den speicher freigeben will hängt sich meine gui/programm auf und im taskmanager wird der speicherverbrauch auch nicht weniger, was ja eigentlich der fall sein sollte wenn ich diesen wieder freigebe. der parser wird während der laufzeit angelegt und nach allen aufgaben wieder gelöscht. folglich wird der destruktor aufgerufen:

Code: Alles auswählen

OSM_Parser::~OSM_Parser()
{
    delete reader;
	delete file;   

	qDebug() << "Destruktor";

	if(!relationContainer.isEmpty())
	{
		qDebug() << "relationContainer";
		QHashIterator<QString, OSM_Relation*> i(relationContainer);
		while (i.hasNext())
		{
			//qDebug() << "delete relation";
			i.next();
			OSM_Relation *relation = i.value();
			delete relation;
		}
		relationContainer.clear();
	}

	if(!wayContainer.isEmpty())
	{
		//qDebug() << "wayContainer";
		QHashIterator<QString, OSM_Way*> x(wayContainer);
		while (x.hasNext())
		{
			//qDebug() << "delete way";
			x.next();
			OSM_Way *way = x.value();
			delete way;
		}
		wayContainer.clear();
	}

	if(!nodeContainer.isEmpty())
	{
		//qDebug() << "nodeContainer";
		QHashIterator<QString, OSM_Node*> k(nodeContainer);
		while (k.hasNext())
		{
			//qDebug() << "delete node";
			k.next();
			OSM_Node *node = k.value();
			delete node;
		}
		nodeContainer.clear();
	}
}
bei kleineren xml dateien scheint es zu funktionieren...

was mache ich falsch bzw kennt jmd ne alternative ?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Wenn das Programm hängt gibt es eigentlich nur eine Möglichkeit - Debuggen...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Troll.Soft
Beiträge: 190
Registriert: 18. Juni 2008 09:52
Wohnort: Hamburg

Beitrag von Troll.Soft »

ich seh im Code kein new.
kater
Beiträge: 306
Registriert: 29. Dezember 2009 01:13
Wohnort: Darmstadt

Beitrag von kater »

Vllt hat das was damit zu tun, dass value() ein const Objekt? zurück gibt. Mein erster Ansatz wäre einfach den [] Operator zu verwenden. Keine Ahnung das steht so in der Doku ^^
Halt, mein aller erster Ansatz wäre QHash::clear(). Der löscht alle Items. Warum dann per Hand machen?
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

ich seh im Code kein new
Was aber kein schlechtes Zeichen iss ^^
Aber wahrscheinlich hat er nur die relevanten Stellen dafür nicht gepostet ^^
was mache ich falsch bzw kennt jmd ne alternative ?
Prinzipielles zum C++ design:

meide new(und delete) wo es geht.
Oft kann man die durch stack objecte und copieren über die Allokatoren(der STL oder von QT) auf den speicher ersetzen.

new und delete einsetzen nur wenn:
- die Objecte ned auf den Stack passen,
- du eine unsbestimmte Anzahl (dynamic) brauchst, die Kopien aber zu teuer(Performance/Ressourcen) werden.

wenn du new und delete (raw) einsetzt ...
- sollten new und delete im selben codeblock, oder in correspondierenden bloecken (create/delete funktion bei factories, construktor/destruktor.. etc) stehen.

Besser: Smartpointer/autopointer nutzen ! (QSharedPtr, boost).

Zu deinem Anwendungsfall:

OSM_Way, was ist das ?
Wie gross ist das, was kostet dich ne tiefe kopie ?
iss das Ding Polymorph ?

Ciao ....
marvel
Beiträge: 105
Registriert: 9. August 2009 07:41

Beitrag von marvel »

ich bin mit meinem latein wirklich am ende. ich habe nun die ganze nacht damit verbracht den fehler zu finden, konnte das problem aber nur ansatzweise einkreisen. bei kleineren xml dateien bzw wenn man nur ein paar tausend objekte erzeugt läuft das programm einwandfrei. im process explorer kann man klar erkennen das sich der speicherverbrauch zwar erhöht, wenn man jedoch die objekte wieder freigibt pendelt es sich wieder ein. wenn man aber die objektanzahl auf 1 mio erhöht, blockt die GUI bei dem punkt wo alle angelegten objekte wieder freigegeben werden sollen. das programm scheint dann eingefroren zu sein, jedoch muss man nur sehr lange warten bis das programm erfolgreich endet. habe extra für die dauer der objekterzeugung und deren verwendung einen eigenen thread implementiert. der debugger läuft auch durch alle freigaben die ich gesetzt habe. ich habe nochmal einen teil des codes gepostet in der hoffnung das ihr vllt mehr erkennt. der parser wird jetzt einmal zu anfang erstellt und erst am ende wenn das programm schliesst wieder gelöscht. da es aber mehrere konvertierungsvorgänge geben kann, rufe ich nach jedem erfolgreichen durchlauf die fkt clear auf. diese soll alle objekte wieder frei geben die während des parsens enstanden sind. jedes im parser erzeugte objekte habe ich mit new erzeugt, weil ich dachte das der heap besser geeignet ist für solche datenmengen.

die 3 klassen/objekte :

Code: Alles auswählen

#ifndef OSM_NODE_H
#define OSM_NODE_H

#include <QString>

class OSM_Node
{
public:
    OSM_Node(QString *lat, QString *lon);
	OSM_Node::~OSM_Node();
    QString* getLatitude();
    QString* getLongitude();

private:
    QString *latitude;
    QString *longitude;
};

#endif // OSM_NODE_H




#ifndef OSM_RELATION_H
#define OSM_RELATION_H

#include <QHash>
#include <QString>

class OSM_Relation
{
public:
    OSM_Relation();
    ~OSM_Relation();
    void fillWayList(QString *way);
    QList<QString*>* getWayList();

private:
    QList<QString*> *wayList;
};

#endif // OSM_RELATION_H




#ifndef OSM_WAY_H
#define OSM_WAY_H

#include <QList>
#include <QString>

class OSM_Way
{
public:
    OSM_Way();
    ~OSM_Way();
    void fillReferenceList(QString *ref);
    QList<QString*>* getReferenceList();

private:
    QList<QString*> *referenceList;
};

im parser werden diese beispielsweise so erzeugt :

Code: Alles auswählen

node = new OSM_Node(new QString(reader->attributes().value("lat").toString()),new QString(reader->attributes().value("lon").toString()));
nodeContainer.insert(new QString(reader->attributes().value("id").toString()),node);

Code: Alles auswählen

way = new OSM_Way();
wayContainer.insert(new QString(reader->attributes().value("id").toString()),way);
way->fillReferenceList(new QString(reader->attributes().value("ref").toString()));

Code: Alles auswählen

relation = new OSM_Relation();
relationContainer.insert(new QString(reader->attributes().value("id").toString()),relation);
rel->fillWayList(new QString(reader->attributes().value("ref").toString()));
wenn der parser abgearbeitet ist soll mit der clear methode alles wieder freigebeben werden

Code: Alles auswählen

void OSM_Parser::clear()
{
    if(!relationContainer.isEmpty())
    {
        qDebug() << "relationContainer";
        QHashIterator<QString*, OSM_Relation*> i(relationContainer);
        while (i.hasNext())
        {
            //qDebug() << "delete relation";
            i.next();
            delete i.key();
            delete i.value();
       
        }
        relationContainer.clear();
    }

    if(!wayContainer.isEmpty())
    {
        //qDebug() << "wayContainer";
        QHashIterator<QString*, OSM_Way*> x(wayContainer);
        while (x.hasNext())
        {
            //qDebug() << "delete way";
            x.next();
            delete x.key();
            delete x.value();
        }
        wayContainer.clear();
    }

    if(!nodeContainer.isEmpty())
    {
        //qDebug() << "nodeContainer";
        QHashIterator<QString*, OSM_Node*> k(nodeContainer);
        while (k.hasNext())
        {
            //qDebug() << "delete node";
            k.next();
            delete k.key();
            delete k.value();
        }
        nodeContainer.clear();
    }
#endif // OSM_PARSER_H
p.s. : habe den code erheblich gekürzt und versucht nur die relevanten informationen reinzunehmen
Zuletzt geändert von marvel am 2. Juni 2010 18:25, insgesamt 5-mal geändert.
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

1. Die deutsche Sprache kennt auch eine Großschreibung.
2. Diese Menge an Code wird sich hier niemand durchsehen und wenn, dann nur als fertige Datei. Und selbst für das fehlt noch die main-Methode.
3. Der 'process explorer' hört sich für mich nicht nach einem Debugger an.
marvel
Beiträge: 105
Registriert: 9. August 2009 07:41

Beitrag von marvel »

so, habe mein programm mal ohne laufzeitumgebung, also direkt über die exe gestartet und siehe da, alles funktioinert einwandfrei. da ich noch nie solch ein großes projekt mit dieser anzahl an daten geschrieben habe, wusste ich nicht das die laufzeitumgebung einfluß auf die ausführung hat.

jetzt stellt sich mir nur noch die frage wann es sinnvoll wäre mit new zu arbeiten und wann nicht. habe letztendlich jedes objekt das ich zwischenspeichere mit new erzeugt. sollte ich da eventuall anders vorgehen, der quellcode ist in meinem post davor.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Prinzipiell zu deinem design:

Code: Alles auswählen

class OSM_Node
{
public:
    OSM_Node(QString *lat, QString *lon);
   OSM_Node::~OSM_Node();
    QString* getLatitude();
    QString* getLongitude();

private:
    QString *latitude;
    QString *longitude;
}; 
warum sind deine Member alle Zeiger ?
QString * sollte man "normal" eher weniger brauchen.

1: QString ist impliziet geshared. Das heisst, Der versucht von Haus aus kopien zu vermeiden.
2: hat NULL bei deinen Werten ne andere Bedeutung als ein Leerstring. Wenn nein, warum Zeiger statt Referenzen ?
3: Wenn Zeiger/Referenz, wer haelt die eigentlichen Objecte ? Wer baut dir deine QStrings ? Sicher das der seine Objecte auch wieder aufraeumt ? Warum halten deine Objecte die Strings ned ?

Zumindest sehen deine Klassen fuer QT eher "unnatürlich" aus

Code: Alles auswählen

class OSM_Node
{
public:
    OSM_Node(const QString &lat, const QString  & lon);
   OSM_Node::~OSM_Node();
    const QString & getLatitude() const;
    QString & getLatitude();
    const QString & getLongitude() const;
    QString & getLongitude();

private:
    QString latitude;
    QString longitude;
}; 
saehe nativer nach QT und c++ code aus :-)

und:

Code: Alles auswählen

node = new OSM_Node(new QString(reader->attributes().value("lat").toString()),new QString(reader->attributes().value("lon").toString())); 
Du erzeugst die die QStrings direkt per einfuegen mit new.
das heisst nur deine OSM_ Klassen bekommen die adressen der zeiger, oder ?
die OSM_Klassen machen selbst aber kein delete ?
zumindest das OSM_Node nirgends ?
Also wo ist das dazugehoerige delete zu deinem new innerhalb deines News ? Also ich würd mich über die speicherleaks ned wundern ^^

Ciao ...
Antworten