QML-Speicherbereinigung löscht noch verwendete Objekte

8

Ich habe dieses Problem mehrmals mit Objekten, die dynamisch erstellt wurden, festgestellt, unabhängig davon, ob sie in QML oder C ++ erstellt wurden. Die Objekte werden gelöscht, während sie noch in Verwendung sind, was ohne ersichtlichen Grund zu harten Abstürzen führt. Die Objekte sind immer noch referenziert und mit anderen Objekten bis hinunter zum Root-Objekt verknüpft. Daher finde ich es seltsam, dass QML diese Objekte löscht, während ihr Refcount immer noch über Null liegt.

Bisher war die einzige Lösung, die ich gefunden habe, die Objekte in C ++ zu erstellen und den Besitz explizit auf CPP zu setzen, was es unmöglich macht, die Objekte aus QML zu löschen.

Zuerst nahm ich an, dass es ein Problem mit der Elternschaft sein könnte, da ich QObject abgeleitete Klassen verwendet habe und die QML-Methode der dynamischen Instanziierung ein Item für ein Elternteil übergibt, während QtObject gar nicht kommt mit einer Elterneigenschaft - es wird nicht von QObject angezeigt.

Aber dann habe ich versucht mit einem Qobject abgeleitet, das Parenting verfügbar macht und benutzt und schließlich sogar probiert, Item zu verwenden, nur um sicher zu gehen, dass die Objekte richtig parented sind, und doch bleibt dieses Verhalten bestehen.

Hier ist ein Beispiel, das dieses Verhalten erzeugt, leider konnte ich es nicht auf eine einzige Quelle reduzieren, weil die tiefe Verschachtelung von Component s es bricht:

%Vor%

Das Beispiel ist ein trivialer Objektbaum-Builder, bei dem die linke Schaltfläche ein Blatt zum Knoten hinzufügt und die rechte Schaltfläche den Knoten einklappt. Um den Fehler zu reproduzieren, genügt es, einen Knoten mit einer Tiefe von 3 zu erstellen und dann den Wurzelknoten zu reduzieren und zu erweitern, worauf die Konsolenausgabe folgendes anzeigt:

%Vor%

Das object des tiefsten Knotens wird ohne Grund gelöscht, obwohl es an den übergeordneten Knoten Item weitergegeben und im JS-Objekt im Listenmodell referenziert wird. Wenn versucht wird, dem tiefsten Knoten einen neuen Knoten hinzuzufügen, stürzt das Programm ab.

Das Verhalten ist konsistent, unabhängig von der Struktur der Struktur, nur die zweite Ebene der Knoten überlebt, alle tieferen Knoten gehen verloren, wenn die Struktur minimiert ist.

Der Fehler liegt nicht in dem Listenmodell, das als Speicher verwendet wird, ich habe mit einem JS-Array und einem QList getestet, und die Objekte sind immer noch verloren. In diesem Beispiel wird ein Listenmodell verwendet, um lediglich die zusätzliche Implementierung eines C ++ - Modells zu speichern. Das einzige Mittel, das ich bis jetzt gefunden habe, war, die Eigentumsrechte der QML an den Objekten überhaupt zu bestreiten. Obwohl dieses Beispiel ein ziemlich konsistentes Verhalten erzeugt, sind im Produktionscode die spontanen Deletionen oft völlig willkürlich.

In Bezug auf den Garbage Collector - ich habe es vorher getestet und festgestellt, dass es recht liberal ist - verursachte das Erstellen und Löschen von Objekten einen 100 MB RAM Wert nicht die Garbage Collection auszulösen, um diesen Speicher freizugeben, und doch in diesem Fall nur ein paar Objekte, die ein paar hundert Bytes wert sind, werden hastig gelöscht.

Gemäß der Dokumentation sollten Objekte, die ein Elternteil haben oder von JS referenziert werden, nicht gelöscht werden, und in meinem Fall sind beide gültig:

  

Das Objekt gehört JavaScript. Wenn das Objekt an QML zurückgegeben wird   Als Rückgabewert eines Methodenaufrufs wird QML diesen zurückverfolgen und löschen   wenn es keine verbleibenden JavaScript-Verweise darauf gibt und es keine hat   QObject :: parent ()

Wie in Filips Antwort erwähnt, geschieht dies nicht, wenn die Objekte von einer Funktion erzeugt werden, die nicht in einem Objekt ist, das gelöscht wird, also etwas mit dem vage erwähnten JS-Zustand zu tun hat, der mit QML-Objekten verbunden ist Ich bin im Wesentlichen noch im Unklaren darüber, warum die Löschung stattfindet, so dass die Frage effektiv noch unbeantwortet bleibt.

Irgendwelche Ideen, was das verursacht?

UPDATE: Neun Monate später gibt es immer noch keine Entwicklung auf diesem kritischen Fehler . Inzwischen habe ich mehrere zusätzliche Szenarien entdeckt, in denen noch verwendete Objekte gelöscht werden, Szenarien, in denen es keine Rolle spielt, wo das Objekt erstellt wurde, und die Umgehung, die Objekte in der Haupt-qml-Datei einfach zu erstellen. Der seltsamste Teil ist, dass die Objekte nicht zerstört werden, wenn sie "nicht referenziert" werden, sondern weil sie "re-referenziert" werden. Das heißt, sie werden nicht zerstört, wenn die visuellen Objekte, die auf sie verweisen, zerstört werden, sondern wenn sie neu erstellt werden.

Die gute Nachricht ist, dass es immer noch möglich ist, die Eigentumsrechte für C ++ auch für Objekte festzulegen, die in QML erstellt werden, so dass die Flexibilität der Objekterstellung in QML nicht verloren geht. Es gibt die kleine Unannehmlichkeit, eine Funktion zum Schutz und zum Löschen jedes Objekts aufzurufen, aber zumindest vermeidet man die fehlerhafte Lebensdauerverwaltung von QtQuick. Ich muss allerdings die "Bequemlichkeit" von QML lieben, da ich gezwungen bin, auf das manuelle Objekt-Lifetime-Management zurückzugreifen.

    
dtech 19.11.2015, 00:00
quelle

2 Antworten

0

QML ist nicht C ++, um Speicher zu verwalten. QML soll darauf achten, Speicher zuzuordnen und freizugeben. Ich denke, das Problem, das Sie gefunden haben, ist nur das Ergebnis davon.

Wenn die dynamische Objekt-Erstellung zu tief geht, scheint alles gelöscht zu sein. Es spielt also keine Rolle, dass Ihre erstellten Objekte Teil der Daten waren - sie sind auch zerstört.

Leider endet mein Wissen hier.

Einer der Umgehungen des Problems (der meine vorherige Aussage beweist) ist das Verschieben der Erstellung von Datenstrukturen aus den XML-Dateien der dynamischen Benutzeroberfläche:

  1. Platzieren Sie die Objekt-Erstellungsfunktion zum Beispiel in main.qml

%Vor%
  1. Verwenden Sie diese Funktion stattdessen in Ihrem Code:

%Vor%     
Filip Hazubski 19.11.2015 08:02
quelle
-1

Erstellen Sie ein Array innerhalb einer .js-Datei und erstellen Sie dann eine Instanz dieses Arrays mit var myArray = []; auf der obersten Ebene dieser .js. -Datei.

Nun können Sie auf jedes Objekt verweisen, das Sie an myArray anhängen, einschließlich solcher, die dynamisch erstellt werden.

JavaScript-Vars werden nicht von der Garbage-Collection gelöscht, solange sie definiert bleiben. Wenn Sie also ein globales Objekt definieren, dann fügen Sie diese Javascript-Datei in Ihr qml-Dokument ein. Sie bleibt so lange wie der Haupt-QML-Bereich.

In einer Datei namens: backend.js

%Vor%

BEARBEITEN :

Lassen Sie mich etwas genauer erklären, wie dies für Ihr bestimmtes Baumbeispiel gelten könnte.

Wenn Sie die Zeile property Item object verwenden, erzwingen Sie versehentlich eine Eigenschaft von Item, die in QML anders behandelt wird. Insbesondere unterliegen Eigenschaften einem eindeutigen Satz von Regeln in Bezug auf Garbage Collections, da die QML-Engine einfach damit beginnen kann, Eigenschaften eines Objekts zu entfernen, um den für die Ausführung erforderlichen Speicher zu verringern.

Fügen Sie stattdessen oben in Ihrem QML-Dokument diese Zeile ein:

%Vor%

Fügen Sie in der Datei object_file.js folgende Zeile ein:

%Vor%

Jetzt können Sie object_hash jederzeit verwenden, um Ihre dynamisch erstellten Komponenten zu speichern und zu verhindern, dass sie durch den Verweis auf

gelöscht werden

object_file.object_hash

Objekt.

Kein Grund sich verrückt zu machen, Besitzer usw. zu ändern.

    
mike510a 19.03.2017 03:02
quelle