GC 崩溃 QML-Application

Posted

技术标签:

【中文标题】GC 崩溃 QML-Application【英文标题】:GC crashes QML-Application 【发布时间】:2016-11-18 13:09:20 【问题描述】:

不要害怕!这不是生产代码。只是为了学习有关 QML 的新东西!我不寻求“你不应该做这样的事情——那样做”。我对 QML 的内部结构更感兴趣

考虑以下 QML 代码

import QtQuick 2.4
import QtQuick.Window 2.0

Window 
    id: root
    width: 800
    height: 600
    visible: true
    GridView 
        width: 800
        height: 200
        model: 4000
        flow: GridView.FlowTopToBottom

        delegate: Rectangle 
            id: myDelegate
            width: 100
            height: 100
            border.width: 1
            Column 
                anchors.fill: parent
                Text 
                    text: index
                    height: 20
                    width: parent.width
                
                Item 
                    id: frame0
                    height: 20
                    width: parent.width
                
                Item 
                    id: frame1
                    height: 20
                    width: parent.width
                
            

            Component.onCompleted: 
                // if (index % 100 === 0) gc()
                frame0.children = [myComp.createObject(myDelegate)]
                frame1.children = [myComp.createObject(null)]
                frame0.children[0].text = 'QML ' + index

                frame1.children[0].text = 'JS ' + index
            
        

        Component 
            id: myComp
            Text 
                anchors.centerIn: parent
                Component.onDestruction: console.log('Destroy ' + text)
            
        
    

它在一定程度上说明了使用动态 ObjectCreation (JS) 时 QML 的 MemoryManagement。 我有一个ListView,它创建了一些代表,让我浏览它们,按需创建新的。

诀窍是:当一个新的委托被创建时,它使用javascript动态对象创建来创建一个文本对象的两个实例。

其中一个是delegate的父级,另一个是null的父级,因此它的生命由JS-Engine决定。它应该被垃圾收集,一旦没有指针离开,指向它。

首先,我将它们都放在frame (Item) 中,以显示(设置视觉父级)。作为委托,这些框架被销毁。 正如预期的那样,这将破坏动态创建的以委托为父对象的对象。另一个是(应该)留给垃圾收集器做他的工作。

这就是它失败的地方 - 有时应用程序在 GC 启动之前崩溃,有时它崩溃了,而 GC 正在尝试做它的工作。

虽然文档不推荐,但手动调用 GC 确实有帮助(激活 Component.onCompleted 中注释掉的行)。

所以在我看来,GC 高估了自己的能力,并决定在已经为时已晚的时候开始行动。

这可能是什么原因?有没有办法告诉 GC 更加积极主动

再次声明:我不打算在我的代码中使用带有.createObject(null) 的动态对象创建。纯属好奇。

【问题讨论】:

在将新创建的对象设置为普通属性时,您是否尝试过这样做?我的猜测是 children 属性是不同的,因为它具有不同的语义 没关系,参考计数在任何情况下都应该起作用。 @ddriver 我的猜测是 QML 引擎期望该对象属于脚本所有权,因为它没有父对象,但实际上属于 C++ 所有权,即被children 删除。因此建议尝试使用单个对象属性,以检查这是否只是通常的“没有父对象,也没有标记为 C++ 所有权但在 C++ 中删除”问题 【参考方案1】:

这可能是什么原因?有没有办法告诉GC 更主动?

原因是 buggy qtquick object lifetime 实现。在这一点上,它看起来不是一个 JS 的东西,而是一个 qtquick 的东西。很明显doesn't abide to its own alleged rules - 例如,它会在仍在使用时删除具有父级的对象,从而导致硬崩溃。您不能期望引用计数也能正常工作。这种行为可能发生在许多使用动态的场景中,它通常不会在琐碎和静态的场景中表现出来。

如链接问题中所述,解决方案是使用手动对象生命周期管理。使用一组新函数来创建和删除对象。

为了创建,您必须将对象传递给 C++ 端,以便在其上调用 QQmlEngine::setObjectOwnership(ojb, QQmlEngine::CppOwnership);,这基本上告诉 qtquick “不要费心去尝试”,幸运的是,至少它可以正常工作 为了销毁,你必须将对象传递给C++端才能调用obj->deleteLater();

对我来说,这就是诀窍,我不再无缘无故地崩溃。使用自定义生命周期管理并远离库存功能。它为您提供保证,该对象将只要您需要它就一直存在,而且它不会停留在您希望它消失的位置,这是另一个问题,尽管不是那么严重.当然,这消除了使用 JS 的便利因素,因为您必须放弃自动生命周期管理并更加勤奋和明确地编写自己的代码,但您无能为力。尽管该错误是在大约一年前报告的并且被认为是严重,但对此并没有做任何工作。因此,我认为,尽管它的严重性可能是关键,但在寻找原因并修复它时,它更像是最低优先级

【讨论】:

您链接到的错误报告似乎假定父项也是 QObject 父项。 IE。它检查 QML parent 标识符/引用,它是父项,而不是 QObject 父项。也许有问题的项目甚至没有 QObject 父级? @KevinKrammer - 这只是为了举例。在我的生产代码中,被删除的对象是不可见的QObject 派生类型,它们将正确的父级接口连接到 QML。它们被一个有效的父级和对它们的多个引用删除。它肯定坏了。 好的,但在错误报告中并非如此。它只检查了parent 属性,它是父项,而不是 QObject 父项。 @KevinKrammer 你开始听起来像一个 Qt 辩护者。也许如果您在急于否认它的缺陷和错误之前花时间进行研究,您会意识到使用createObject(validParentItem)parentparentItem 设置为同一个对象,所以不,也许它没有。此外,还有一点额外的内容,你会意识到我只使用了Item 作为示例,以使其简单明了,没有额外的用户代码,如果不是其他各种半生不熟的方面,我会使用一个裸露的QtObject Qt 的例如不连接它已经可用的父接口到 qml... 我还没有研究过 createObject() 的实现,它可能会或可能不会设置 QObject 父级。我只是说错误报告不包含对 QObject 父级的检查。特别是因为我同意这是人们所期望的,所以我认为指出事实并非如此是很重要的。我了解到,在使用 Repeater 时,它是其委托的 QObject 父项,但其父项是父项

以上是关于GC 崩溃 QML-Application的主要内容,如果未能解决你的问题,请参考以下文章

突然弹出王者荣耀停止运行,GC超时导致的后台应用崩溃问题分析

调试 Python 致命错误:已跟踪 GC 对象

System.gc()与Runtime.gc()的区别

Java之GC 如何工作

java面试-GC

Java中GC (Allocation Failure)日志分析实战