QIcon/QImage是否存在内存泄漏问题?

3

我使用Qt 4.8.1和MSVC 2008时,遇到了一些使用QIcon时的内存泄漏问题。
这种情况发生在简单的QAction(例如菜单条目,甚至是Qt Designer自动生成的那些)或容器项(例如QTreeWidgetItem)中。
例如:

QTreeWidgetItem *newItem = new QTreeWidgetItem();
newItem->setText(0, "Item");

// This causes a memory leak!
newItem->setIcon(0, QIcon("D:\\Dnl\\QtSandBoxApp\\Resources\\dataset2.png"));

treeWidget->addTopLevelItem(newItem);

经过大量的调试,我发现QIcon内部使用了一个QImage,这个对象似乎被正确地销毁了(引用计数器降为零)。下面是一个简单的示例:
#include "stdafx.h"
#include <QtGui/QApplication>
#include <crtdbg.h>

int main(int argc, char *argv[])
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    QApplication a(argc, argv);
    //QPixmapCache::setCacheLimit(0);

    // MEMORY LEAK!
    // Internally uses QImageReader
    QImage image("D:\\Dnl\\QtSandBoxApp\\Resources\\dataset2.png");

    // NO MEMORY LEAK!
    //QImage image(QSize(16, 16), QImage::Format_ARGB32);
    return 0;
}

我有什么遗漏吗?
这是一个非常烦人的问题,因为它会防止正确跟踪内存泄漏(即由应用程序而非Qt引起的内存泄漏)。

编辑:这是内存泄漏报告。

Detected memory leaks!
Dumping objects ->
{5637} normal block at 0x00A46020, 76 bytes long.
 Data: <d ,g ^      L Bg> 64 D2 2C 67 E8 5E A4 00 00 00 00 00 4C B9 42 67 
{5636} normal block at 0x00A45EE8, 12 bytes long.
 Data: < z   `   z  > 14 7A D3 00 20 60 A4 00 00 7A D3 00 
{5634} normal block at 0x00A46148, 128 bytes long.
 Data: <    6   6   Za  > 01 00 00 00 36 00 00 00 36 00 00 00 5A 61 A4 00 
{5614} normal block at 0x00A45DC8, 76 bytes long.
 Data: <d ,g x      L Bg> 64 D2 2C 67 E8 78 A4 00 00 00 00 00 4C B9 42 67 
{5613} normal block at 0x00A478E8, 12 bytes long.
 Data: <     ]      > A4 96 CA 00 C8 5D A4 00 90 96 CA 00 
{5611} normal block at 0x00A45C48, 128 bytes long.
 Data: <    6   5   Z\  > 01 00 00 00 36 00 00 00 35 00 00 00 5A 5C A4 00 
{5591} normal block at 0x00A477C8, 76 bytes long.
 Data: <d ,g u      L Bg> 64 D2 2C 67 10 75 A4 00 00 00 00 00 4C B9 42 67 
{5590} normal block at 0x00A47510, 12 bytes long.
 Data: <     w      > B8 96 C6 00 C8 77 A4 00 A4 96 C6 00 
{5588} normal block at 0x00A45B88, 128 bytes long.
 Data: <    6   5    [  > 01 00 00 00 36 00 00 00 35 00 00 00 9A 5B A4 00 
{5566} normal block at 0x00A473F0, 76 bytes long.
 Data: <d ,g s      L Bg> 64 D2 2C 67 A8 73 A4 00 00 00 00 00 4C B9 42 67 
{5565} normal block at 0x00A473A8, 12 bytes long.
 Data: <     s      > E0 D7 C3 00 F0 73 A4 00 CC D7 C3 00 
{5563} normal block at 0x00A471E0, 128 bytes long.
 Data: <    6   5    q  > 01 00 00 00 36 00 00 00 35 00 00 00 F2 71 A4 00 
{5543} normal block at 0x00A47008, 76 bytes long.
 Data: <d ,g o      L Bg> 64 D2 2C 67 C0 6F A4 00 00 00 00 00 4C B9 42 67 
{5542} normal block at 0x00A46FC0, 12 bytes long.
 Data: <La   p  8a  > 4C 61 BB 00 08 70 A4 00 38 61 BB 00 
{5540} normal block at 0x00A46DF8, 128 bytes long.
 Data: <    6   6    n  > 01 00 00 00 36 00 00 00 36 00 00 00 0A 6E A4 00 
{5520} normal block at 0x003ED9A8, 76 bytes long.
 Data: <d ,g` >     L Bg> 64 D2 2C 67 60 D9 3E 00 00 00 00 00 4C B9 42 67 
{5519} normal block at 0x003ED960, 12 bytes long.
 Data: <      >     > A4 C6 B4 00 A8 D9 3E 00 90 C6 B4 00 
{5517} normal block at 0x00A46D38, 128 bytes long.
 Data: <    6   5   Jm  > 01 00 00 00 36 00 00 00 35 00 00 00 4A 6D A4 00 
{5496} normal block at 0x003ED8D8, 76 bytes long.
 Data: <d ,g  >     L Bg> 64 D2 2C 67 80 D7 3E 00 00 00 00 00 4C B9 42 67 
{5495} normal block at 0x003ED780, 12 bytes long.
 Data: <<     > (   > 3C CA 00 10 D8 D8 3E 00 28 CA 00 10 
{5491} normal block at 0x00A412A0, 128 bytes long.
 Data: <    6   5       > 01 00 00 00 36 00 00 00 35 00 00 00 B2 12 A4 00 
{5260} normal block at 0x00A48448, 64 bytes long.
 Data: <            Z   > 01 00 00 00 16 00 00 00 13 00 00 00 5A 84 A4 00 
{5233} normal block at 0x00A48380, 56 bytes long.
 Data: <        Ha    Bg> 00 00 CD 00 C0 82 A4 00 48 61 A4 00 04 BA 42 67 
{5231} normal block at 0x00A482C0, 128 bytes long.
 Data: <    6   6       > 01 00 00 00 36 00 00 00 36 00 00 00 D2 82 A4 00 
{4681} normal block at 0x00A48150, 64 bytes long.
 Data: <            b   > 01 00 00 00 16 00 00 00 13 00 00 00 62 81 A4 00 
{4655} normal block at 0x00A47FB8, 56 bytes long.
 Data: <     ~  H\    Bg> 00 00 CA 00 F8 7E A4 00 48 5C A4 00 04 BA 42 67 
{4653} normal block at 0x00A47EF8, 126 bytes long.
 Data: <    5   5       > 01 00 00 00 35 00 00 00 35 00 00 00 0A 7F A4 00 
{4102} normal block at 0x00A47DB8, 64 bytes long.
 Data: <             }  > 01 00 00 00 16 00 00 00 13 00 00 00 CA 7D A4 00 
{4075} normal block at 0x00A47D40, 56 bytes long.
 Data: <     {   [    Bg> 00 00 C6 00 D8 7B A4 00 88 5B A4 00 04 BA 42 67 
{4073} normal block at 0x00A47BD8, 126 bytes long.
 Data: <    5   5    {  > 01 00 00 00 35 00 00 00 35 00 00 00 EA 7B A4 00 
{3522} normal block at 0x00A479F8, 64 bytes long.
 Data: <             z  > 01 00 00 00 16 00 00 00 13 00 00 00 0A 7A A4 00 
{3496} normal block at 0x00A47AA0, 56 bytes long.
 Data: <    8y   q    Bg> 00 00 BD 00 38 79 A4 00 E0 71 A4 00 04 BA 42 67 
{3494} normal block at 0x00A47938, 126 bytes long.
 Data: <    5   5   Jy  > 01 00 00 00 35 00 00 00 35 00 00 00 4A 79 A4 00 
{2943} normal block at 0x00A41220, 64 bytes long.
 Data: <            2   > 01 00 00 00 16 00 00 00 13 00 00 00 32 12 A4 00 
{2917} normal block at 0x00A40418, 56 bytes long.
 Data: <    X    m    Bg> 00 00 B6 00 58 03 A4 00 F8 6D A4 00 04 BA 42 67 
{2915} normal block at 0x00A40358, 128 bytes long.
 Data: <    6   6   j   > 01 00 00 00 36 00 00 00 36 00 00 00 6A 03 A4 00 
{2367} normal block at 0x00A40760, 64 bytes long.
 Data: <            r   > 01 00 00 00 16 00 00 00 13 00 00 00 72 07 A4 00 
{2341} normal block at 0x00A401D0, 56 bytes long.
 Data: <    h   8m    Bg> 00 00 B4 00 68 00 A4 00 38 6D A4 00 04 BA 42 67 
{2339} normal block at 0x00A40068, 126 bytes long.
 Data: <    5   5   z   > 01 00 00 00 35 00 00 00 35 00 00 00 7A 00 A4 00 
{1788} normal block at 0x00A40820, 64 bytes long.
 Data: <            2   > 01 00 00 00 16 00 00 00 13 00 00 00 32 08 A4 00 
{1762} normal block at 0x00A417B0, 56 bytes long.
 Data: <              Bg> 00 00 00 10 B0 18 A4 00 A0 12 A4 00 04 BA 42 67 
{1760} normal block at 0x00A418B0, 126 bytes long.
 Data: <    5   5       > 01 00 00 00 35 00 00 00 35 00 00 00 C2 18 A4 00 
{252} normal block at 0x003E8A08, 40 bytes long.
 Data: <        @B      > 00 00 00 00 CC CD CD CD 40 42 0F 00 00 00 00 00 
{251} normal block at 0x003E8950, 120 bytes long.
 Data: <Lc-g  >     L Bg> 4C 63 2D 67 08 89 3E 00 00 00 00 00 4C B9 42 67 
{250} normal block at 0x003E8908, 8 bytes long.
 Data: < c-gP > > 0C 63 2D 67 50 89 3E 00 
{249} normal block at 0x003E88A0, 40 bytes long.
 Data: <        @B      > 00 00 00 00 CC CD CD CD 40 42 0F 00 00 00 00 00 
{248} normal block at 0x003E8828, 60 bytes long.
 Data: <      >         > 07 00 00 00 08 89 3E 00 80 06 00 00 00 CD CD CD 
Object dump complete.
The program '[2808] QtSandBoxApp.exe: Native' has exited with code 0 (0x0).

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 这会报告在运行应用程序时检测到的任何内存泄漏。 - XDnl
可能是重复问题?http://stackoverflow.com/questions/3865773/qt-memory-management-whats-wrong - cmannett85
嗯...我认为我的示例更加简洁,而且检测内存泄漏的测试也不同。 - XDnl
你能否编辑你的问题,展示出最后一个例子中的内存泄漏调试输出? - cmannett85
有趣的是,如果我加载两个(不同的)QImage,内存泄漏的数量并不会增加。 - XDnl
显示剩余2条评论
2个回答

5
我解决了这个问题。
使用CrtDbg库API与Qt一起使用似乎不是一个好主意。
引用自http://winfig.com/?p=154

Qt在其DLL中分配内存,而此内存在卸载DLL后释放。 不幸的是,这是在Visual Studio报告所谓的泄漏之后。

事实上,当我从磁盘请求QImage时,以下模块会在运行时加载:
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\plugins\imageformats\qgifd4.dll'
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\plugins\imageformats\qicod4.dll'
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\plugins\imageformats\qjpegd4.dll'
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\plugins\imageformats\qmngd4.dll'
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\plugins\imageformats\qsvgd4.dll'
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\bin\QtSvgd4.dll', Symbols loaded.
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\bin\QtXmld4.dll', Symbols loaded.
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\plugins\imageformats\qtgad4.dll'
'QtSandBoxApp.exe': Loaded 'C:\Libraries\Qt\4.8.1\plugins\imageformats\qtiffd4.dll'

Visual Leak Detector 正确地报告了没有内存泄漏。

P.S: 这篇 QtCentre 的维基条目是误导的 :P


我找到了两个版本的VLD。https://github.com/oneiric/vld是更新的版本,而https://kinddragon.github.io/vld则有一个市场扩展。 - Burak

1

另一种避免内存泄漏的方法是使用QIcon调用QPixmap,在您的示例中如下所示:

QTreeWidgetItem *newItem = new QTreeWidgetItem();
newItem->setText(0, "Item");

// This causes a memory leak!
newItem->setIcon(0, QIcon(QPixmap("D:\\Dnl\\QtSandBoxApp\\Resources\\dataset2.png")));

treeWidget->addTopLevelItem(newItem);

如我在我的博客中所解释的(https://programmersexception.blogspot.com/2019/04/memory-leak-con-qicon-en-qt-594-y.html),很抱歉是用西班牙语编写的,有一个已知的BUG,我希望他们能尽快修复(https://bugreports.qt.io/browse/QTBUG-59621)。


1
链接很好,但如果您能总结一种可能的解决问题的方法,那将更有帮助。 - Shanteshwar Inde
在这种情况下,进一步的细节将始终在错误文档中,我认为重复BUG信息并不是一个好主意,因为你可以在那里找到关于它的大量讨论(超过10条评论)。 - Gasteizko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接