当动态创建的对象有父对象时,QML 不会释放大量内存

Posted

技术标签:

【中文标题】当动态创建的对象有父对象时,QML 不会释放大量内存【英文标题】:QML does not free a lot of memory, when dynamically created object has a parent 【发布时间】:2017-08-09 11:17:40 【问题描述】:

我使用Component.createObject() 方法来动态创建一个对象。之后,我使用 destroy() 方法删除对象。 如果我在没有父参数(带有 null)的情况下调用方法 Component.createObject(null),那么 destroy() 方法会释放内存。如果我用一些parent 调用该方法,那么destroy() 方法不会释放大量内存。当创建大量对象时,这会导致超出 Windows 操作系统中每个进程允许的内存量,并导致应用崩溃。 重现问题的代码:main.qml

import QtQuick 2.8

Item 
    visible: true
    width: 640
    height: 480

    Scene 
        id: scene
        anchors.fill: parent
    

Scene.qml

import QtQuick 2.8

Item 
    id: scene

    Component.onCompleted: 
        var tileComponent = Qt.createComponent("Tile.qml");

        for (var i = 0; i < 200000; ++i) 
            var tile = tileComponent.createObject(scene);
//          var tile = tileComponent.createObject(null); // this works well, but I need a parent
            tile.destroy();
        
    

Tile.qml

import QtQuick 2.8

Rectangle 
    width: 100
    height: 100

    color: "orange"

destroy() 之后,有父代的变体使用 40 MB 内存,没有父代 - 12 MB。如果我继续创建和销毁此类对象,使用的内存将继续增长。 如何动态创建和销毁大量具有父对象的对象以避免内存问题? 我已经在 Windows 7 x64 上测试了 Qt 5.9.1(MinGW 5.3.0 编译器)和 Qt 5.8.0(MSVC 2015)。

【问题讨论】:

我只能猜测:也许parent 包含对某个包装器的引用,该包装器未被删除。 我试图删除父级本身(动态创建场景,然后调用destroy()),但没有帮助。 【参考方案1】:

这种行为对于 Qt 来说是正常的。你不需要做任何事情。如果您继续测试,您会在某个时候看到内存使用量停止增长。

我建议不要使用gc(),因为它确实没有任何帮助。它强制 Qt 做一些它会在需要时自动做的事情。

垃圾收集器可以通过在 javascript 中调用 gc() 来手动调用。这将导致执行一个全面的收集周期,这可能需要几百到一千多毫秒才能完成,因此应尽可能避免。

source

【讨论】:

我现在又测试了一次:当点击键时,开始创建新的大部分对象。你是绝对正确的!内存使用量在 130 MB 时停止增长。第一次,当我测试类似的情况时,我非常非常快地创建了新对象,所以没有足够的时间来释放内存并且应用程序崩溃了。谢谢,菲利普! 不要相信自动的gc()。当您看起来合适时手动调用它,因为它有时真的很懒惰,并且可能会认为简单地使您的应用程序崩溃而不是完成这项工作。特别是当您希望它自动 delete() 没有引用的对象时。 另一个问题是在我的真实代码中。它与 QML 中的这种情况有关:var testObj = 'a': 10, 'b': 20, 'c': 60, 'd': 70; for (var key in testObj) delete testObj[key]; 此代码将仅删除 2 个元素,但不会全部删除。纯 javascript 将删除所有元素。【参考方案2】:

QML 并不是很喜欢释放内存,但你也可以通过调用 gc() 来强制释放它,而且按顺序调用它通常会有好处,它会一直挤压额外的内存,直到第 3 次调用。

从好的方面来说,如果您创建更多对象而不是分配更多内存,则使用的内存将被重用。

总而言之,QML 是一个内存猪。即使从 QML 创建简单的、不可见的 QObjects 也有 significant overhead。所以我建议,如果可能的话,使用一些 C++ 驱动的模型,而不是将成千上万的 QML 对象放在内存中、延迟加载、按需加载——这样的技术将为您的内存使用创造奇迹。

【讨论】:

看来gc()只会为没有父对象的对象释放内存:QML memory management。 gc() 代表“垃圾收集”——它不涉及任何对象,它涉及不再被任何对象使用的内存。请记住,QML 远非完美无缺,众所周知,实际上会删除仍在使用的对象bugreports.qt.io/browse/QTBUG-50319,所以不要指望奇迹。我已经用父母和数百兆内存的大量对象进行了测试,并且 gc() 有效 我需要动态创建和删除很多对象,但同时在场景中只能有大约100个可见对象。所以,我只能创建一些对象池,然后我将更改未使用对象的属性,而不是创建/删除新对象。 @BlackHoleTracer 这个想法很糟糕。您期望 Qt 会尽快释放所有未使用的内存,但 Qt 的方法有些不同。只有当它真的需要时,它才会释放这个内存。你不需要关心这个。每分钟创建和删除数千个对象,您的应用程序将正常工作。 答案可能取决于平台。在 android 上,QML 很容易耗尽内存。例如,播放的音频文件只会保留在内存中,直到最终在 Android 上出现错误 -19。因此,如果您的应用播放大量音频文件,您将不得不接管内存管理。

以上是关于当动态创建的对象有父对象时,QML 不会释放大量内存的主要内容,如果未能解决你的问题,请参考以下文章

Java方法内创建对象实例后,啥时候释放内存(引

释放自动释放对象不会使我的应用程序崩溃,为啥?

动态内存

QML 对象不更新显示

如何在 QML 中的同一事件之后创建/销毁动态对象?

动态内存——动态内存与智能指针