Seite 1 von 1

Speicher wird nicht freigegeben

Verfasst: 2. Juni 2010 01:01
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 ?

Verfasst: 2. Juni 2010 07:34
von Christian81
Wenn das Programm hängt gibt es eigentlich nur eine Möglichkeit - Debuggen...

Verfasst: 2. Juni 2010 10:40
von Troll.Soft
ich seh im Code kein new.

Verfasst: 2. Juni 2010 11:13
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?

Verfasst: 2. Juni 2010 14:36
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 ....

Verfasst: 2. Juni 2010 15:03
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

Verfasst: 2. Juni 2010 16:46
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.

Verfasst: 2. Juni 2010 19:11
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.

Verfasst: 7. Juni 2010 10:42
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 ...