QML garbage collection deletes objects still in use

I ran into this problem several times when objects were created dynamically, regardless of whether they were created in QML or C ++. Objects are deleted during use, resulting in severe crashes for no apparent reason. Objects still refer to parent objects and are deferred to other objects, so I find it strange that QML deletes these objects and their refcount is still above zero.

So far, the only solution I have found was to create objects in C ++ and explicitly assign ownership of CPP, which makes it impossible to delete objects from QML.

At first, I suggested that this might be a problem with the parent position, since I used QObject derived classes, and the QML dynamic creation method passes Item to the parent, while QtObject does not even come with the parent property - it does not appear from QObject .

But then I tried using the QObject derivative, which exposes and uses parental control, and finally even tried to use Item just to make sure that the objects are correctly parental, and yet this behavior is still preserved.

Here is an example that produces this behavior, unfortunately, I could not smooth it to a single source, because the deep nesting of Component violates it:

 // ObjMain.qml Item { property ListModel list : ListModel { } Component.onCompleted: console.log("created " + this + " with parent " + parent) Component.onDestruction: console.log("deleted " + this) } // Uimain.qml Item { id: main width: childrenRect.width height: childrenRect.height property Item object property bool expanded : true Loader { id: li x: 50 y: 50 active: expanded && object && object.list.count width: childrenRect.width height: childrenRect.height sourceComponent: listView } Component { id: listView ListView { width: contentItem.childrenRect.width height: contentItem.childrenRect.height model: object.list delegate: Item { id: p width: childrenRect.width height: childrenRect.height Component.onCompleted: Qt.createComponent("Uimain.qml").createObject(p, {"object" : o}) } } } Rectangle { width: 50 height: 50 color: "red" MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { if (mouse.button == Qt.RightButton) { expanded = !expanded } else { object.list.append({ "o" : Qt.createComponent("ObjMain.qml").createObject(object) }) } } } } } // main.qml Window { visible: true width: 1280 height: 720 ObjMain { id: obj } Uimain { object: obj } } 

An example is a trivial object tree builder, with the left button adding the sheet to the node, and the right button to the node. All that is required to reproduce the error is to create a node with a depth of 3, and then collapse and expand the root of the node, which displays the console output:

 qml: created ObjMain_QMLTYPE_0(0x1e15bb8) with parent QQuickRootItem(0x1e15ca8) qml: created ObjMain_QMLTYPE_0(0x1e5afc8) with parent ObjMain_QMLTYPE_0(0x1e15bb8) qml: created ObjMain_QMLTYPE_0(0x1e30f58) with parent ObjMain_QMLTYPE_0(0x1e5afc8) qml: deleted ObjMain_QMLTYPE_0(0x1e30f58) 

object the deepest node is deleted for no reason, even if it has a parent node Item and is specified in the JS object in the list model. Attempting to add a new node to the deepest node will crash the program.

The behavior is consistent, regardless of the structure of the tree, only the second level of nodes survives, deeper and deeper nodes are lost when the tree collapses.

The error is not in the list model used as storage, I tested it with a JS and QList array and the objects are still lost. This example uses a list model to preserve an additional implementation of the C ++ model. The only tool I have found so far has been to completely deny QML being an object. Although this example provides fairly consistent behavior, spontaneous deletions in production code are often completely arbitrary.

As for the garbage collector - I tested it before and noticed that it is quite liberal - creating and deleting objects, 100 MB of RAM did not cause garbage collection to release this memory, and yet in this case there are only a few objects worth several hundred bytes, hastily removed.

According to the documentation, objects having a parent or referencing JS should not be deleted, and in my case both are valid:

The object belongs to JavaScript. When an object returns to QML as the return value of a method call, QML will track it and delete it if there are no JavaScript references on it and no QObject :: parent ()

As mentioned in Philippe's answer, this does not happen if the objects are created by a function that is not in the object that is being deleted, so it may have something to do with the undefined JS state associated with QML objects, but I'm essentially still in unaware of why the deletion occurs, so the question still remains unanswered.

Any ideas what causes this?

UPDATE: Nine months later, still zero development, this critical mistake . In the meantime, I found several additional scripts in which objects that are still in use are deleted, scripts in which it does not matter where the object was created, and a workaround for simply creating objects in the main qml file is not applied. The strangest part is objects that are not destroyed when they are “unconnected”, but because they are “re-referenced”. That is, they are not destroyed when visual objects that reference them are destroyed, but when they are recreated.

The good news is that you can still establish ownership of C ++ even for objects created in QML, so the flexibility of creating objects in QML is not lost. There is a slight inconvenience to calling a function to protect and delete each object, but at least you avoid QtQuick error management. Gotta love the "convenience" of QML, but - forced return to manual control over the life cycle of an object.

+8
javascript garbage-collection qt qml qtquick2
source share
2 answers

QML is not C ++ for memory management. QML is designed to take care of memory allocation and freeing it. I think the problem you found is only the result of this.

If the dynamic creation of objects is too deep, everything seems remote. Therefore, it doesn’t matter that your created objects were part of the data - they are also destroyed.

Unfortunately, my knowledge ends here.

One of the problems associated with the problem (proving my previous statement) is to move the creation of the data structure from the dynamic UI qml files:

  • Place an object creation function, for example, in main.qml

 function createNewObject(parentObject) { parentObject.list.append({ "o" : Qt.createComponent("ObjMain.qml").createObject(parentObject) }) } 
  1. Use this function instead of code:

 // fragment of the Uimain.qml file MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { if (mouse.button == Qt.RightButton) { expanded = !expanded } else { createNewObject(object) } } } 
0
source share

Create an array inside the .js file, and then create an instance of this array with var myArray = []; at the top level of this .js. file .js. .

Now you can reference any object that you add to myArray , including those that are created dynamically.

Javascript vars are not removed by garbage collection as long as they remain defined, so if you define them as a global object, then include this Javascript file in your qml document, it will remain as long as the main QML is in the area.


In a file named: backend.js

 var tiles = []; function create_square(new_square) { var component = Qt.createComponent("qrc:///src_qml/src_game/Square.qml"); var sq = component.createObject(background, { "backend" : new_square }); sq.x = new_square.tile.x sq.y = new_square.tile.y sq.width = new_square.tile.width; sq.height = new_square.tile.height; tiles[game.board.getIndex(new_square.tile.row, new_square.tile.col)] = sq; sq.visible = true; } 

EDIT :

Let me explain a little more clearly how this applies to your particular tree.

Using the string property Item object , you inadvertently force it to be an Item element, which is handled differently in QML. In particular, properties fall under a unique set of rules in terms of garbage collections, since the QML mechanism can simply start deleting the properties of any object to reduce the amount of memory needed to run.

Instead, at the top of your QML document, specify this line:

 import "./object_file.js" as object_file 

Then, in the object_file.js file , specify this line:

  var object_hash = []; 

Now you can use object_hash at any time to save your dynamically created components and prevent them from disappearing by specifying

object_file.object_hash

an object.

No need to go crazy changing owners, etc.

-one
source share

All Articles