Speicherplatz eines Model/View Modells

Alles rund um die Programmierung mit Qt
realdarkman71
Beiträge: 15
Registriert: 21. Oktober 2010 00:15

Speicherplatz eines Model/View Modells

Beitrag von realdarkman71 »

Hallo Leute,

ich habe ein QTableView das die Daten per QStandardItemModel erhält. Ich fülle das Model in diesem Schema:

Code: Alles auswählen

QList<QStandardItem *> column1, column2, column3;

(...)

colmun1.append(new QStandardItem("blah"));
(...)
colmun2.append(new QStandardItem("blah"));
(...)
colmun3.append(new QStandardItem("blah"));

(...)

model->appendColumn(column1);
model->appendColumn(column2);
model->appendColumn(column3);

model->setHeaderData(0, Qt::Horizontal, "column 1");
model->setHeaderData(0, Qt::Horizontal, "column 2");
model->setHeaderData(0, Qt::Horizontal, "column 3");

(...)

tableView->setModel(model);
Beim Füllen des Models werden ja nun viele QStandardItems erzeugt und dem Model hinzugefügt. Wenn ich das im Programm öfters aufrufe, braucht das Programm natürlich immer mehr Speicher. Wie kann ich das lösen? Ein delete object funktioniert ja auch nicht, da die QStandardItems noch gebraucht werden! Ein model-clear() rufe ich schon auf, immer bevor das Model neu befüllt wird. Speicherplatz steigt trotzdem stetig an!

Kann mir da jemand einen Tipp geben!?

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

Beitrag von Christian81 »

Die Standarditems werden bei einem clear() gelöscht. Daran liegt es wohl nicht. Der Speicher muss woanders verloren gehen. Also entweder Code (minimales Beispiel zum Nachstellen) oder z.B. unter Linux mit valgrind schauen was passiert.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
realdarkman71
Beiträge: 15
Registriert: 21. Oktober 2010 00:15

Beitrag von realdarkman71 »

Ok, das model->clear() die enthaltenen QStandardItems löscht ist schon mal gut, war mir da nicht sicher. Es heißt ja immer, was man mit new anlegt, muss man auch mit delete wieder löschen! ...aber dieses valgrind ist mier zu hoch! Gibt es da Tutorials o.ä. ?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
realdarkman71
Beiträge: 15
Registriert: 21. Oktober 2010 00:15

Beitrag von realdarkman71 »

Ich habe das Programm mal mit valgrind laufen lassen. Direkt nach dem Start kommt gleich der erste Fehler (obwohl ich noch gar nichts gemacht habe!):
==12333== Conditional jump or move depends on uninitialised value(s)
==12333== at 0x5273611: inflateReset2 (in /lib/libz.so.1.2.3.4)
==12333== by 0x52736EC: inflateInit2_ (in /lib/libz.so.1.2.3.4)
==12333== by 0x5273762: inflateInit_ (in /lib/libz.so.1.2.3.4)
==12333== by 0x5256F13: png_create_read_struct_2 (in /lib/libpng12.so.0.44.0)
==12333== by 0x52570F9: png_create_read_struct (in /lib/libpng12.so.0.44.0)
==12333== by 0x428E8F3: ??? (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x428F32A: ??? (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x4257EDB: QImageReader::read(QImage*) (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x4258516: QImageReader::read() (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x426EB6A: QPixmapData::fromFile(QString const&, char const*, QFlags<Qt::ImageConversionFlag>) (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x426924F: QPixmap::load(QString const&, char const*, QFlags<Qt::ImageConversionFlag>) (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x42694AC: QPixmap::QPixmap(QString const&, char const*, QFlags<Qt::ImageConversionFlag>) (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
Dann habe ich nur eine Aktion ausgeführt:
==12350== HEAP SUMMARY:
==12350== in use at exit: 2,089,605 bytes in 22,237 blocks
==12350== total heap usage: 446,438 allocs, 424,201 frees, 50,700,615 bytes allocated
==12350==
==12350== LEAK SUMMARY:
==12350== definitely lost: 3,812 bytes in 39 blocks
==12350== indirectly lost: 11,040 bytes in 565 blocks
==12350== possibly lost: 1,437,001 bytes in 14,918 blocks
==12350== still reachable: 637,752 bytes in 6,715 blocks
==12350== suppressed: 0 bytes in 0 blocks
==12350== Rerun with --leak-check=full to see details of leaked memory
==12350==
==12350== For counts of detected and suppressed errors, rerun with: -v
==12350== Use --track-origins=yes to see where uninitialised values come from
==12350== ERROR SUMMARY: 202962 errors from 599 contexts (suppressed: 259 from 12)
...bloß eine Aktion und schon 202962 Errors!!! Da ist mein Programm ja der größte Memory-Leak den es gibt!?

Ich glaube, ich lass' das dann mal lieber mit dem Programmieren! :cry:
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Ein wenig sollte man sich schon mit valgrind beschäftigen *bevor* man damit was anfangen kann...
Wirkliche Probleme sehe ich da nicht - schon gar nicht solche wie Du im ersten Post beschrieben hast.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
realdarkman71
Beiträge: 15
Registriert: 21. Oktober 2010 00:15

Beitrag von realdarkman71 »

...und woher kommen dann die vielen Fehlermeldungen??? Ich habe nur mit dem system-monitor gesehen, dass der Speicherbedarf immer wächst, beim Arbeiten mit dem Programm! Daher müssen ja wohl einige Objekte nicht gelöscht werden (o.ä.)!?
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Es heißt ja immer, was man mit new anlegt, muss man auch mit delete wieder löschen!
richtig. gutes Design waere dann, das new und das dazugehoerige delete im selben context zu finden sind. also Ctor /Dtor einer Klasse, im selben block, oder eben ueber Smartpointer.
Generell sollte man bei c++ sowieso rohe news vermeiden.
Erfahrene c++ programmierer sollten deshalb recht wenig probs mit leaks haben, also theorethisch.

Nun kommt aber die Qt, da kann man gutes c++ design vergessen ^^ weil Qt oft zwingend new vorraussetzt, und eben selber extensiv gebraucht von new macht.

Was ich hauptsaechlichst mit sagen will:

normal sollte ein c++ programmierer den Speicher im kopf quasi ueberwachen koennen. wenn Du ein new siehst, solltest du sofort wissen, wo das dazugehoerige delete passiert. Oftmals muss man dann nur den Programmfluss ueberfliegen um zu schauen ob das delete aufgerufen wird.
Eigentlich sollte das bei normalen c++ gar kein Problem sein. Da gehoert nur bissi uebung zu.
Bei Qt wird halt alles bissi komplizierter, aber die generellen regeln bleiben gleich. Es kommen halt nur noch paar Komponenten zu, die man ned unter kontrolle hat.
Wie gesagt, 1. ansatz waere, das du jedes new in deinem code markierst, und schaust wo das delete dazu aufgerufen wird. dann checkst, ob er auch in den code reinlaeuft.
Ich habe nur mit dem system-monitor gesehen, dass der Speicherbedarf immer wächst, beim Arbeiten mit dem Programm!
Das muss nicht zwingend sein, das es alles speicherleaks sind. die STL allokiert speicher z.b. sehr progressiv fuer ihre container ....
Aber zu erkennen, ob der speicherverbrauch da von leaks kommt, oder die STL / Qt nur mal richtig zulangt ist ned einfach .... gibt aber paar tricks zu.
Einfacher isses aber wirklich nen Controllprog fuern speicher zu verwenden. Valgrind sollte dir verlaesslicher sagen koennen, ob leaks hasst.

Ciao ...
realdarkman71
Beiträge: 15
Registriert: 21. Oktober 2010 00:15

Beitrag von realdarkman71 »

Erstmal danke für Deine ausführliche Antwort!
Einfacher isses aber wirklich nen Controllprog fuern speicher zu verwenden. Valgrind sollte dir verlaesslicher sagen koennen, ob leaks hasst.
Ist denn valgrind so ein "Controllprog" zum Überwachen des Speichers oder gibt es da noch andere Programme?
realdarkman71
Beiträge: 15
Registriert: 21. Oktober 2010 00:15

Beitrag von realdarkman71 »

Wie sind denn solche Errors zu deuten?
==12333== Conditional jump or move depends on uninitialised value(s)
==12333== at 0x5273611: inflateReset2 (in /lib/libz.so.1.2.3.4)
==12333== by 0x52736EC: inflateInit2_ (in /lib/libz.so.1.2.3.4)
==12333== by 0x5273762: inflateInit_ (in /lib/libz.so.1.2.3.4)
==12333== by 0x5256F13: png_create_read_struct_2 (in /lib/libpng12.so.0.44.0)
==12333== by 0x52570F9: png_create_read_struct (in /lib/libpng12.so.0.44.0)
==12333== by 0x428E8F3: ??? (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x428F32A: ??? (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x4257EDB: QImageReader::read(QImage*) (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x4258516: QImageReader::read() (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x426EB6A: QPixmapData::fromFile(QString const&, char const*, QFlags<Qt::ImageConversionFlag>) (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x426924F: QPixmap::load(QString const&, char const*, QFlags<Qt::ImageConversionFlag>) (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
==12333== by 0x42694AC: QPixmap::QPixmap(QString const&, char const*, QFlags<Qt::ImageConversionFlag>) (in /opt/qt-4.7.1/lib/libQtGui.so.4.7.1)
In dieser Meldung kommt ja mein Programm nicht vor, ist der Fehler dann bei Qt?

Und an mehreren Stellen meckert er "Conditional jump or move depends on uninitialised value(s)", schaue ich dann in die entsprechende Codezeile (hier meint er die 2. Zeile) steht dort aber nur eine einfache Variablenzuweisung, wie z.B.

Code: Alles auswählen

QByteArray bytes = qProcess->readAllStandardOutput();
QStringList lines = QString(bytes).split("\n");
ist denn an dem Code etwas falsch?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

RHBaum hat geschrieben:Nun kommt aber die Qt, da kann man gutes c++ design vergessen ^^ weil Qt oft zwingend new vorraussetzt, und eben selber extensiv gebraucht von new macht.
Man könnte das natürlich umgehen, wenn man statt rohen Zeigern in Qt auf Klassen mit Handles o.Ä. gesetzt hätte. Dann darf man aber einen Reference-Counter mitschleppen, und Ableitungen von QWidget sind auch nicht mehr so straight-forward.
Ein großes Problem ist aber Polymorphie. QStandardItemModel erlaubt z.B. ABleitungen der QStandardItems. Würde SIModel eine Liste mit Values (also QList <STandardItem>) speichern, hast du wunderbares Slicing. Selbes auch bei QLayout, welches die gelayouteten Widgets auch in ner dynamischen Liste aka QList o.Ä. speichern muss. Da Qt extensiv auf Polymorphie setzt muss man wohl oder übel irgendwo Zeiger einsetzen.

ABER: Eigentlich steht in der Doku immer dabei, wer den Besitz übernimmt:
void QStandardItemModel::setItem ( int row, int column, QStandardItem * item )
Sets the item for the given row and column to item. The model takes ownership of the item.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Conditional jump or move depends on uninitialised value(s)
Heisst im Klartext:
Du springst in abhaengigkeit irgend einer variable (if/then;switch; etc) irgendwelchen code an, und diese variable ist niemals initialisiert wurden !

Kann ein Hinweiss auf ne Fehlerquelle sein, muss aber nicht.
Man kann Variablen auch im C-Style belegen ueber memset / memcpy z.b. Sowas kann das tool einfach ned erkennen ....

In deinem beispiel wird mit sicherheit irgend eine c-lib verwendet ... inflate klingt nach zlib oder ner anderen Kompressionslib. Grad im zusammenhang mit c und assembler wird dir nen tool wie valgrind oft nur hinweisse, keine 100% Analyse liefern koennen.
Ich wuerd hier fast sicher auf Fehlalarm tippen !

Noch ne Anmerkung zu franzf's Posting:

Wir leben in keiner perfekten Welt.
Wir wissen, gutes Design kostet, kann kosten ....
Kosten schraenken aber die Verwendungsmöglichkeiten wiederum ein.
Man muss also irgendwelche Kompromisse immer machen !!!

Ciao ...
realdarkman71
Beiträge: 15
Registriert: 21. Oktober 2010 00:15

Beitrag von realdarkman71 »

...also ist eine Zuweisung wie

Code: Alles auswählen

QStringList lines = QString(bytes).split("\n"); 
so in Ordung?
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Ich weiss ned was "bytes" ist, aber tendentiell schaut es gut aus ....

>> QStringList lines = QString(bytes).split("\n");

>> QString(bytes)
Du erstellst ein Temporaeres QString Object, was den Inhalt von bytes uebernimmt, sprich am Ende irgendwie kopiert ...

>> .split("\n");
Du führst daran die Spilt funktion aus, diese liefert dir eine Liste mit QStrings der einzelnen Abschnitte. Du kannst dir sicher sein, dass die Einzelstrings kopien und keine referenzen auf irgendwas sind.
Der uebergabeparamater "\n" ist nen literal aus dem const bereich, der bleibt immer geultig ^^ mit dem bekommst also auch keine Probs. Muesste aber soweiso nur solange halten bis die split funktion durch ist ..

>> QStringList lines =
Du weisst das Ergebniss einer eigenen Variable vom Typ stringslist zu ...
Das ist eine Kopie, die durch das impliziete sharen sicher vermieden wird, aber fuer dich ist es technisch wie eine Kopie ....

erst nach der zuweissung wird dein temporaeres QString(bytes) wieder zerstoert, aber dein Ergebniss QStringList lines kann ohne die Vorlage auch gut leben, da es nur kopien verwendet ....

Bei der Qt ist "solcher code" durchaus gewollt ...

Ciao ...
realdarkman71
Beiträge: 15
Registriert: 21. Oktober 2010 00:15

Beitrag von realdarkman71 »

bytes ist die Ausgabe eines Kommandozeilenprogramms mittles QProcess. Das Ergebis möchte ich zeilenweise parsen, daher die QStringList! Diese gehe ich danach mit foreach durch!
Antworten