Memory Leak bei Standard Tree Model ?

Alles rund um die Programmierung mit Qt
Antworten
hunsrus
Beiträge: 18
Registriert: 16. Juli 2008 11:54

Memory Leak bei Standard Tree Model ?

Beitrag von hunsrus »

Hallo Leute!

ich brauche eure Hilfe. Seit einigen Tagen hab ich das Problem, dass meine Anwendung unglaublich viel Speicherhungrig ist, obwohl eigentlich nichts besonderes damit gemacht wird. Das Programm bietet zwei TreeViews, denen ich ein abgeleitetes QStandardItemModel unterbaue. Wenn ich von einem tree ein item in das andere dragge und dort droppe, wird immer mehr speicher reserviert - was ja erstmal auch okay ist. aber müsste dieser speicher nicht wieder freigegeben werden, wenn ich das item lösche!?!??!?!? es handelt sich um ein abgeleitetes QStandardItem, das ich beim drag'n'drop erstelle. ich habs mit removeRow() und removeRows() versucht um alle items zu löschen - ey - je mehr ich lösche, desto mehr speicher wird verbraten. ich fass es nicht. hinter jedem item steckt übrigens ein objekt, das aber beim löschvorgang immer mitgelöscht wird. kennt das jemand von euch? ich hab echt keinen plan mehr was ich machen soll...

bei bedarf poste ich etwas code.

viele liebe grüße

Sebastian
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Solche Probleme kenn ich nicht, und es würd mich wundern, wenn das ein Speicherleck in den Qt-Libs wäre.

Also poste Code (alles von Relevanz), dann kann man mehr sagen.

Grüße
Franz
hunsrus
Beiträge: 18
Registriert: 16. Juli 2008 11:54

Beitrag von hunsrus »

Hi!

also dann poste ich mal den meiner ansicht nach wichtigen code. Die Kommentare sollten helfen, es leichter zu verstehen. In der überschriebenen Funktion dropMimedata() lese ich das Mimedataobjekt aus, das ich beim draggen erstelle. Die gedraggten items, oder vielmehr die Objekte, die dahinter stehen, werden dann gemarkert...

Code: Alles auswählen

bool CMyTreeModel::dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
{

	CMyMimeData *mmd = (CMyMimeData*) data;
	/*beim drag wird ein abgeleitetes mimedata-objekt angelegt, dem ich die ausgewählten
	modelindexes gebe. das kann dann hier einfach ausgelesen werden.
	ich halte das für ne einfachere lösung als die stream-methode von qt...*/
	QModelIndexList mixlist = mmd->indexes();
	DRAGDROPTYPE ddt = mmd->getDDT();

	if (ddt == DDT_TREE)/*woher kommen die daten?*/
	{
		if (m_bUpperTree)/*wird auf den ersten tree gedropp?*/
		{
			/*items intern verschieben*/
			return true;
		}//m_bUpperTree
		else/*wird auf den zweiten tree gedroppt?*/
		{
			for (int s = 0; s < mmd->indexes().size(); s++)
			{
				CMyObject* pObject;
				CMyTreeItem *myitem = (CMyTreeItem*)m_pUserMenuCallback->m_TreeModel->itemFromIndex(mmd->indexes()[s]);
				pObject = myitem->m_NodeLection;//mynode->m_pLection;
				if (pObject!=NULL){
					pObject->setMarkiert(true);/*hier markiere ich das objekt, damit es auch im zweiten view erscheint*/
					reset();
				}
			}
			m_pUserMenuCallback->updateSecondView();/*die markierten objekte auflisten*/
			return true;
		}
	}

	return false;


}
...sodass sie von der untenstehenden methode erkannt und als item im anderen treeview dargestellt werden können. das mache ich wie folgt:

Code: Alles auswählen

void CUserMenu::updateSecondView()
{
		m_Lections.clear();
		m_TreeModel_lowertree->removeRows(0,m_TreeModel_lowertree->rowCount());/*alle items löschen*/


		/*hier mach ich noch ein paar push_back()s für ein paar in einem vectorcontainer als pointer
		gespeicherte pointer. ich markiere beim drag-and-drop-prozess die gedraggten objekte und
		iteriere dann durch den container mit diesen objekten um die relevanten objekte als items 
		in meinem zweiten treeview darzustellen.*/
		
		for (int a = 0; a < m_Objects.size(); a++)
		{
			if (m_Objects[a]->getStatus())
			{
				CMyTreeItem *lt_item = new CMyTreeItem(NULL);
				lt_item->SetObject(m_Objects[a]);
				lt_item->setText(m_Objects[a]->GetName());
				lookForChildren(lt_item);/*rekursive funktion, die unterobjekte findet und an das übergebene Item als subitems anhängt*/
				CMyTreeItem* lt_rootitem = (CMyTreeItem*)m_TreeModel_lowertree->invisibleRootItem();
				lt_rootitem->appendRow(lt_item);
			}
		}
}
Dieser code wird immer aufgerufen, wenn sich etwas ändert - etwa ein item im ersten treeview (der mit allen items) umbenannt wird oder eines gelöscht wird.

Die rekursive Funktion

Code: Alles auswählen

void CUserMenu::lookForChildren(CMyTreeItem* parent)
{
	int v = 0;

	for (v = 0; v < parent->GetNodeLection()->m_pSubLectionVList.size(); v++)
	{
		CMyTreeItem* newsub = new CMyTreeItem(this);
		newsub->SetNodeLection(parent->GetNodeLection()->m_pSubLectionVList[v]);
		newsub->SetObject(parent->GetObject()->m_SubObjectVList[v]);/*jedes objekt kann weitere container enthalten!!!*/
		/*parentitem setzen!*/
		newsub->setParentItem(parent);
		parent->appendRow(newsub);
		lookForChildren(newsub);
	}

}
ermöglicht die darstellung von verzweigungen.

Ich weiß nicht, ob es vielleicht eine andere, einfache(re) lösung gibt, QStandardItems (also die abgeleiteten) zu "filtern" und die gedroppten in einer zweiten view anzuzeigen. Ich halte dies für die am einfachsten zu verstehende art - doch leider machen die memoryleaks einen strich durch die rechnung. Diese MemoryLeaks sind übrigens nur in der Release-exe gut zu beobachten; im debugmodus läuft alles "normal".
Qt kümmert sich doch alleine um erzeugte Qt-Objekte und löscht ungebrauchte wieder, oder? Gilt das auch für abgeleitete?

Vielen Dank für eure Hilfe!

Sebastian
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

hunsrus hat geschrieben:Qt kümmert sich doch alleine um erzeugte Qt-Objekte und löscht ungebrauchte wieder, oder? Gilt das auch für abgeleitete?
Das stimmt leider nur halb... Von QObject abgeleitete Objekte werden zerstört, wenn ein parent() existiert und dieses zerstört wird. QStandardItem ist kein QObject.

Bau dir doch einen einfachen Testcase, mit 10 Items pro View. Gib in den Destruktor aller deiner abeleiteten Klassen eine debug-Message, die eindeutig sagt, was das für ein Item ist (->setData()).

Ich denke da liegt der Hund begraben...

Grüße
Franz
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

ich habe keine Erfahrung im QStandardItemModel, aber laut Doku hat das kein "removeRows()". Die Basisklasse QAbstractItemModel hat sowas, aber diese Implementierung tut nichts ("The base class implementation does nothing and returns false."). Oder hast du diese Methode selbst implementiert?

Btw, da gibt es noch viele Schwächen im Code:
* Verwende dynamic_cast und Q_ASSERTs anstelle C-Casts!
* Verwende Signals/Slots für Callbacks, keine zyklomatischen Abhängigkeiten!!
hunsrus
Beiträge: 18
Registriert: 16. Juli 2008 11:54

Beitrag von hunsrus »

Vielen Dank für die schnellen Antworten. Ich werd morgen versuchen, euren Tipps zu folgen.
Das mit dem removeRows()... das ist mir gar nicht aufgefallen! allerdings verschwinden die items, die ich so lösche. Heißt das etwa, dass sie nur unsichtbar gemacht werden?! und: wie genau muss der code aussehen, der in der überschriebenen Funktion drinsteht? muss ich dort etwa jedes item explizit mit delete löschen?

Liebe Grüße

Sebastian
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

removeRow(s) arbeitet laut Sourcen wie erwarten -> es löscht die Einträge
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

tatsächlich... removeRows() ist im Source vorhanden.. wieder was gelernt :wink:
hunsrus
Beiträge: 18
Registriert: 16. Juli 2008 11:54

Beitrag von hunsrus »

Ja also ich hatte bisher leider keine zeit das problem weiter zu verfolgen. Vielleicht wirds ja über das lange osterwochenende was. nach dem was ich den beiden letzten beiträgen entnehmen kann, lohnt es sich nicht, eine eigene removeRow(s)() methode zu implementieren?

ich wünsche euch ein frohes, gesegnetes Ostern und ein geruhsames langes wochenende!


Sebastian
Antworten