Unsolved Right way to delete resources
-
I'm not sure if I'm doing it correcly.
In aQMainWindow
application I create and populate tabs for aQWidgetTab
at runtime. Then I need to free the resources to create a new set.Here how I create them:
void MainWindow::createTabs() { QStringList names; // contains the list of the tab's name foreach (QString name, names) { QWidget *widget = new QWidget(); QScrollArea *scrollArea = new QScrollArea(); scrollArea->setWidget(widget); loadTab(view, widget, name); ui->tabView->addTab(scrollArea, name); } } void MainWindow::loadTab(MainWindow::Views view, QWidget *widget, QString tab) { FlowLayout *layout = new FlowLayout(); layout->setOrientation(Qt::Vertical); widget->setLayout(layout); populateTab(view, tab, widget); } void MainWindow::populateTab(MainWindow::Views view, QString tab, QWidget *parent) { QMutableMapIterator<int, DatabaseManager::Data> i(_mapData[tab]); // contains the information to display controls while (i.hasNext()) { i.next(); DatabaseManager::Data data = i.value(); QGroupBox *group = retrieveGroup(data.group, tab, parent); QFormLayout *formLayout = qobject_cast<QFormLayout *>(group->layout()); QLineEdit *widget = new QLineEdit(); data.widget = widget; i.setValue(data); QLabel *label = new QLabel(data.description); formLayout->addRow(label, data.widget); } } QGroupBox *MainWindow::retrieveGroup(QString name, QString tab, QWidget *parent) { if (_mapGroups[tab].contains(name)) return _mapGroups[tab][name]; QGroupBox *group = new QGroupBox(name, parent); QFormLayout *formLayout = new QFormLayout(); group->setLayout(formLayout); parent->layout()->addWidget(group); _mapGroups[tab][name] = group; return group; }
And here how I'm trying to free resources:
for (int i = 0; i < ui->tabView->count(); i++) { qDeleteAll(ui->tabView->widget(0)->findChildren<QWidget *>("", Qt::FindDirectChildrenOnly)); delete ui->tabView->widget(0); } ui->tabView->clear(); _mapGroups.clear();
But I see a memory leakage: every time I load the very same set of tabs the memory occupation of my process increases.
I tried to change this:qDeleteAll(ui->tabView->widget(0)->findChildren<QWidget *>("", Qt::FindDirectChildrenOnly));
to
qDeleteAll(ui->tabView->widget(0)->findChildren<QWidget *>("", Qt::FindChildrenRecursively));
but I got a crash in Qt code.
How would you delete all the newly crated objects? -
@Mark81
I believe you have two problems here....- When you parent a widget like a tab to a TabWidget, when the tabwidget goes out of scope, it's destructor is called. If you're trying to delete
the tab from the tabwidget, it would be a double delete because when setlayout is called, it automatically makes the children tabs parented to the tabwidget which autmatically
"deletes" its children when it goes out of scope. - That kind of leads into 2... that means if that tabwidget exists for the lifetime of the program, you will see your "memory go up." even though you removed the widget etc.
To free memory in the middle of the program I think you would need to unparent the child widget... then keep a pointer to the address then call delete.
I may be wrong because I am a newbie so take what I say with a grain of salt.
Also, a quick answer from a google search explains point 2 a bit further.
"It is not necessary that your operating system memory manager will release each and every deallocated heap memory byte. Usually it does not. But that doesn't mean that the memory has leaked.
For example, you may begin with 5 mb of memory usage, then allocate another 5 mb worth of objects, and when deallocated it only frees 3 mb and you are left with 7 mb usage. But if you allocate the 5 mb worth of objects again, memory usage will not jump to 12 mb, it will still jump to 10, because those 2 unreleased mb will be reused.
Take a look here, where I investigated one such issue. As you can see, for a while the memory usage keeps growing, from 41 to 53 mb, but then it stabilizes and it doesn't grow further even after hundreds of allocations/deallocations. Different OS memory managers work in a different way, the amount total memory and free memory are also a factor, possibly the application's own usage patterns as well.
It can be regarded as a leak only if each time you do that the memory usage increases by 2 mb and never reaches a stable level. If you make a custom widget with a debug message in the destructor, you can verify when and whether it and its children are being destroyed. But again, there is no guarantee that you reclaim 100% of the used memory."
- When you parent a widget like a tab to a TabWidget, when the tabwidget goes out of scope, it's destructor is called. If you're trying to delete
-
@JahJerMar Thanks a lot for your answer.
Some more details:-
I do have a memory leak: every time I allocate the same set of tabs the memory usage increase of about 2.1 MB and going on with free resources and allocate again it reaches hundreds of MB.
-
About
QTabWidget
: I'm not sure to understand. I know that when I remove a tab it does not delete the content, this is why I callqDeleteAll
and thendelete
the widget. But do this two commands ensure that all the children objects are deleted as well? -
When my tabs were populated with simpler widgets (i.e. just a
QWidget
, aQVBoxLayout
and plenty ofQLabels
) the code above worked and the memory usage didn't grow.
I'm afraid about all the nested objects I create:
- many
QScrollArea()
as tabs - a
QWidget()
as area's tab - a
FlowLayout()
as widget's layout - many
QGroupBox(widget)
in the layout - a
QFormLayout()
as group's layout - many
QLineEdit ()
in the layout
My code pretends to delete all the above items, cycling among each tab and deleting the "direct children" and the tab itself (the
QScrollArea
object). I've already tried to add a debug line in the destructor ofFlowLayout
: it is actually called, though less times as I expect (i.e. I have 16 tabs, hence 16FlowLayout
but I get only 8 debug messages).Because all of them are inside layouts and the layouts are assigned to widgets, deleting the widgets does not delete also their layouts and the contained objects?
-
-
Something you. I was confused by my code revision and I forgot a line in the code:
QLineEdit *widget = new QLineEdit(); data.widget = widget; i.setValue(data);
this save the pointer to the newly created widget into my
Data
struct.
Actually, after deleting the resources those pointers are still valid! I mean they still points to the widgets - I bet here is the problem.
But adding this before the code already posted above:QMapIterator<QString, QMap<int, DatabaseManager::Data>> i1(_mapData); while (i1.hasNext()) { i1.next(); QMapIterator<int, DatabaseManager::Data> i2(i1.value()); while (i2.hasNext()) { i2.next(); delete i2.value().widget; // this is the pointer to the created widgets! } }
leads to a segmentation fault on the
delete
call. -
With this code the memory usage increases now of 40-50 kB each time (instead of 2.1 MB):
QMutableMapIterator<QString, QMap<int, DatabaseManager::Data>> i1(_mapData); while (i1.hasNext()) { i1.next(); QMutableMapIterator<int, DatabaseManager::Data> i2(i1.value()); while (i2.hasNext()) { i2.next(); if (i2.value().widget) { DatabaseManager::Data data = i2.value(); data.widget->deleteLater(); data.widget = nullptr; i2.setValue(data); } } } QMutableMapIterator<QString, QMap<QString, QGroupBox *>> i3(_mapGroups); while (i3.hasNext()) { i3.next(); QMutableMapIterator<QString, QGroupBox *> i4(i3.value()); while (i4.hasNext()) { i4.next(); qDeleteAll(i4.value()->findChildren<QWidget *>("", Qt::FindDirectChildrenOnly)); i4.value()->deleteLater(); } } for (int i = 0; i < ui->tabView->count(); i++) { QScrollArea *a = qobject_cast<QScrollArea *>(ui->tabView->widget(0)); qDeleteAll(a->widget()->findChildren<QWidget *>("", Qt::FindDirectChildrenOnly)); qDeleteAll(ui->tabView->widget(0)->findChildren<QWidget *>("", Qt::FindDirectChildrenOnly)); delete ui->tabView->widget(0); } ui->tabView->clear();
-
@Mark81 said in Right way to delete resources:
i4.value()->findChildren<QWidget *>("", Qt::FindDirectChildrenOnly)
Are you sure this actually finds anything?