PDA

View Full Version : QIcon has increased memory usage because pixmaps are not loaded on demand?



m_markus
28th February 2017, 10:12
Hello,

Im currently updating my application from Qt 5.2.1 with MinGW 4.8 to Qt 5.7 with MinGW 5.3. I am using the precompiled packages from the online installer under Windows 7. If I compile with Qt 5.7 I see a significant increase of memory consumption during runtime in the task manager from around 70 MB with Qt 5.2.1 to 380 MB with Qt 5.7. So I tried to find the reason for that behaviour.

I found that the QComboBox method http://doc.qt.io/qt-5/qcombobox.html#addItem-1 overload that takes a QIcon is increasing the memory usage. After testing around, I think the memory increased is caused because http://doc.qt.io/qt-5/qicon.html#addFile creates pixmaps for every Icon Mode and Icon State and load it into memory at initialisation and not like the documentation mentioned on demand.

Adds an image from the file with the given fileName to the icon, as a specialization for size, mode and state. The file will be loaded on demand.

I searched the internet about this problem, but the only thing I found was https://forum.qt.io/topic/72240/qicon-not-loading-qpixmap-on-demand. That suprised me, because a increase of the memory footprint for about 300% isn't something that you miss as a developer.

I prepared a minimal example thats shows the problem. I use PNG Files attached to the Qt resource system, but you can use files in the application directory as well.



#include <QApplication>
#include <QWidget>
#include <QComboBox>
#include <QVBoxLayout>

int main(int argc, char *argv[])
{
//QCoreApplication::setAttribute(Qt::AA_EnableHighDp iScaling, true);
//QCoreApplication::setAttribute(Qt::AA_UseHighDpiPi xmaps, false);

QApplication a(argc, argv);

bool useIcon = true;
bool usePixmap = false;
QWidget mainWidget;
QVBoxLayout *layout = new QVBoxLayout;
for(int i = 0; i < 20; ++i)
{
QComboBox *box = new QComboBox;
box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
if(useIcon)
{
if(usePixmap)
{
box->addItem(QIcon(QPixmap(":/img/about-icon.png")), QObject::tr("Box %1 - 1").arg(i));
box->addItem(QIcon(QPixmap(":/img/android-add-contact.png")), QObject::tr("Box %1 - 2").arg(i));
box->addItem(QIcon(QPixmap(":/img/Bug.png")), QObject::tr("Box %1 - 3").arg(i));
box->addItem(QIcon(QPixmap(":/img/database-symbol-md.png")), QObject::tr("Box %1 - 4").arg(i));
box->addItem(QIcon(QPixmap(":/img/Devices-printer-icon.png")), QObject::tr("Box %1 - 5").arg(i));
}
else
{
box->addItem(QIcon(":/img/about-icon.png"), QObject::tr("Box %1 - 1").arg(i));
box->addItem(QIcon(":/img/android-add-contact.png"), QObject::tr("Box %1 - 2").arg(i));
box->addItem(QIcon(":/img/Bug.png"), QObject::tr("Box %1 - 3").arg(i));
box->addItem(QIcon(":/img/database-symbol-md.png"), QObject::tr("Box %1 - 4").arg(i));
box->addItem(QIcon(":/img/Devices-printer-icon.png"), QObject::tr("Box %1 - 5").arg(i));
}
}
else
{
box->addItem(QObject::tr("Box %1 - 1").arg(i));
box->addItem(QObject::tr("Box %1 - 2").arg(i));
box->addItem(QObject::tr("Box %1 - 3").arg(i));
box->addItem(QObject::tr("Box %1 - 4").arg(i));
box->addItem(QObject::tr("Box %1 - 5").arg(i));
}
layout->addWidget(box);
}
mainWidget.setLayout(layout);
mainWidget.show();

return a.exec();
}


I compiled a realease build and running this example with Qt 5.2.1 and Qt 5.7. on Windows 7. The memory consumption is measured using the windows task manager. These are the results:


useIcon = true, usePixmap = false:

Qt 5.2.1: 5740 KB
Qt 5.7: 46472 KB

useIcon = true, usePixmap = true:

Qt 5.2.1: 7184 KB
Qt 5.7: 9712 KB

useIcon = false:

Qt 5.2.1: 4636 KB
Qt 5.7: 8096 KB



My question are:

Can you reproduce this behaviour?
Is there a flag that controls if the pixmaps are loaded on demand or not?
Is there a workaround or should I fill out a bug report?


Thank you very much.
~ Markus

m_markus
10th March 2017, 14:02
Is there anyone who can help or even reproduce this? Please let me know if I provided not enough informations.
Im currently try to write my own QIconEngine as a workaround.

anda_skoa
11th March 2017, 12:02
Can you attach a buildable example as a ZIP archive?

Maybe in a way that takes the two bools from commandline args the program can be quickly run through all options?

If this is a bug that would also help the bug report as any respective developer would have a test case at hand.

Cheers,
_

m_markus
13th March 2017, 14:18
Thank you very much for your response. I attaches a zip file with the buildable example. It has two boolean command line options:

-i or --icon : use the QIcon constructor
-p or --pixmap : use the QPixmap constructor

If you set both, the QIcon constructor will be used, if you set none no icons will be loaded.


#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QComboBox>
#include <QVBoxLayout>
#include <QCommandLineParser>
#include <QCommandLineOption>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QCommandLineParser parser;
parser.setApplicationDescription("A test to observe memory usage of QIcon.");
parser.addHelpOption();

QCommandLineOption useIconOption(QStringList() << "i" << "icon", "Use the QIcon constructor");
parser.addOption(useIconOption);
QCommandLineOption usePixmapOption(QStringList() << "p" << "pixmap", "Use the QPixmap constructor");
parser.addOption(usePixmapOption);

parser.process(a);

bool useIcon = parser.isSet(useIconOption);
bool usePixmap = parser.isSet(usePixmapOption);

QWidget mainWidget;
QVBoxLayout *layout = new QVBoxLayout;
if(useIcon)
{
layout->addWidget(new QLabel("Using Icon Contructor"));
}
else if(usePixmap)
{
layout->addWidget(new QLabel("Using Pixmap Contructor"));
}

for(int i = 0; i < 20; ++i)
{
QComboBox *box = new QComboBox;
box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
if(useIcon)
{
box->addItem(QIcon(":/img/about-icon.png"), QObject::tr("Box %1 - 1").arg(i));
box->addItem(QIcon(":/img/android-add-contact.png"), QObject::tr("Box %1 - 2").arg(i));
box->addItem(QIcon(":/img/Bug.png"), QObject::tr("Box %1 - 3").arg(i));
box->addItem(QIcon(":/img/database-symbol-md.png"), QObject::tr("Box %1 - 4").arg(i));
box->addItem(QIcon(":/img/Devices-printer-icon.png"), QObject::tr("Box %1 - 5").arg(i));
}
else if(usePixmap)
{
box->addItem(QIcon(QPixmap(":/img/about-icon.png")), QObject::tr("Box %1 - 1").arg(i));
box->addItem(QIcon(QPixmap(":/img/android-add-contact.png")), QObject::tr("Box %1 - 2").arg(i));
box->addItem(QIcon(QPixmap(":/img/Bug.png")), QObject::tr("Box %1 - 3").arg(i));
box->addItem(QIcon(QPixmap(":/img/database-symbol-md.png")), QObject::tr("Box %1 - 4").arg(i));
box->addItem(QIcon(QPixmap(":/img/Devices-printer-icon.png")), QObject::tr("Box %1 - 5").arg(i));
}
else
{
box->addItem(QObject::tr("Box %1 - 1").arg(i));
box->addItem(QObject::tr("Box %1 - 2").arg(i));
box->addItem(QObject::tr("Box %1 - 3").arg(i));
box->addItem(QObject::tr("Box %1 - 4").arg(i));
box->addItem(QObject::tr("Box %1 - 5").arg(i));
}
layout->addWidget(box);
}
mainWidget.setLayout(layout);
mainWidget.show();

return a.exec();
}

m_markus
21st March 2017, 12:44
I created a bug report:
https://bugreports.qt.io/browse/QTBUG-59621