Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Delete a dynamically-loaded QQuickItem
Forum Updated to NodeBB v4.3 + New Features

Delete a dynamically-loaded QQuickItem

Scheduled Pinned Locked Moved QML and Qt Quick
10 Posts 3 Posters 7.2k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    jrlafff
    wrote on last edited by
    #1

    Hi,

    I have an application that creates QQuickItems from QML files and adds them as visual children of a general item.

    The pseudo-code is as follows:

    QQmlComponent * pQMLComponent = new QQmlComponent( engine, QUrl::fromLocalFile("mycomponent.qml") );
    QObject * pObject = pQMLComponent->create ();
    pQuickItem = qobject_cast<QQuickItem*>( pObject );
    pQuickItem->setParentItem ( pQuickParent ); // Sets the visual parent item
    ...

    At some point, I would like to dispose of this component as I no longer need it. I tried calling:

    delete pQuickItem;
    pQuickItem = NULL;

    But this crashes quite often on the first line.

    I then tried calling:

    pQuickItem->setParent( NULL );
    pQuickItem->setParentItem( NULL );
    pQuickItem->deleteLater();

    This does not crash but it does not seem to have any effect. I see that the component gets hidden, but my tests show that its destroyed() signal is never called, and the memory is never reclaimed, while the control returns to the event loop and I wait several minutes.

    This is a problem for me as I regularly create items and thus memory is leaking.

    How can I properly delete such a component?

    B 1 Reply Last reply
    0
    • J jrlafff

      Hi,

      I have an application that creates QQuickItems from QML files and adds them as visual children of a general item.

      The pseudo-code is as follows:

      QQmlComponent * pQMLComponent = new QQmlComponent( engine, QUrl::fromLocalFile("mycomponent.qml") );
      QObject * pObject = pQMLComponent->create ();
      pQuickItem = qobject_cast<QQuickItem*>( pObject );
      pQuickItem->setParentItem ( pQuickParent ); // Sets the visual parent item
      ...

      At some point, I would like to dispose of this component as I no longer need it. I tried calling:

      delete pQuickItem;
      pQuickItem = NULL;

      But this crashes quite often on the first line.

      I then tried calling:

      pQuickItem->setParent( NULL );
      pQuickItem->setParentItem( NULL );
      pQuickItem->deleteLater();

      This does not crash but it does not seem to have any effect. I see that the component gets hidden, but my tests show that its destroyed() signal is never called, and the memory is never reclaimed, while the control returns to the event loop and I wait several minutes.

      This is a problem for me as I regularly create items and thus memory is leaking.

      How can I properly delete such a component?

      B Offline
      B Offline
      Buttink
      wrote on last edited by Buttink
      #2

      @jrlafff I am pretty sure you are inadvertently given QML ownership of your object. see http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership for what I am talking about.

      EDIT: nope I read to fast.
      EDIT2: I don't know without code you could be dealing with the issue above (because of the crash).

      1 Reply Last reply
      0
      • J Offline
        J Offline
        jrlafff
        wrote on last edited by jrlafff
        #3

        Here is the code, simplified:

        for ( int i = 0; i < 10000; i++ )
        {
        	QQmlComponent * pQMLComponent = new QQmlComponent( engine, QUrl::fromLocalFile("mycomponent.qml") );
        	QObject * pObject = pQMLComponent->create ();
        	QQuickItem * pQuickItem = qobject_cast<QQuickItem*>( pObject );
        	//pQuickItem->setParentItem ( pQuickParent ); // Sets the visual parent item - issue persists when no parent is set
        
        	delete pQMLComponent;
        
        	pQuickItem->setParent( NULL );
        	pQuickItem->setParentItem( NULL );
        	pQuickItem->deleteLater();
        }
        

        I made a loop here only to show that 25MB of memory is consumed. This memory is never released.

        The content of the mycomponent.qml file is:

        import QtQuick 2.2
        
        Item {
        	
        }
        
        B 1 Reply Last reply
        0
        • J jrlafff

          Here is the code, simplified:

          for ( int i = 0; i < 10000; i++ )
          {
          	QQmlComponent * pQMLComponent = new QQmlComponent( engine, QUrl::fromLocalFile("mycomponent.qml") );
          	QObject * pObject = pQMLComponent->create ();
          	QQuickItem * pQuickItem = qobject_cast<QQuickItem*>( pObject );
          	//pQuickItem->setParentItem ( pQuickParent ); // Sets the visual parent item - issue persists when no parent is set
          
          	delete pQMLComponent;
          
          	pQuickItem->setParent( NULL );
          	pQuickItem->setParentItem( NULL );
          	pQuickItem->deleteLater();
          }
          

          I made a loop here only to show that 25MB of memory is consumed. This memory is never released.

          The content of the mycomponent.qml file is:

          import QtQuick 2.2
          
          Item {
          	
          }
          
          B Offline
          B Offline
          Buttink
          wrote on last edited by
          #4

          @jrlafff Oh ok so there is a difference between your QQuickItem::setParentItem and QObject::setParent. QQuickItem::setParentItem() sets the visual parent where QObject::setParent sets the parent ownership. Parent ownership dictates that when a QObject is deleted it deletes its children. Now granted I'm not possitive why deleteLater doesn't seem to work, but your object doesn't have a parent to delete it nor are you just calling delete on that pointer. So instead try doing

          for ( int i = 0; i < 10000; i++ )
          {
              QQmlComponent * pQMLComponent = new QQmlComponent( engine, QUrl::fromLocalFile("mycomponent.qml") );
              QObject * pObject = pQMLComponent->create ();
              QQuickItem * pQuickItem = qobject_cast<QQuickItem*>( pObject );
              pQuickItem->setParent(pQuickParent); // Set owning parent
              pQuickItem->setParentItem ( pQuickParent ); // Sets the visual parent item - issue persists when no parent is set
          
              delete pQMLComponent; // you really should consider using a smart pointer
          }
          

          Now if pQuickParent ever gets deleted (by like being unloaded from a loader) it will be freed automatically for you.

          1 Reply Last reply
          0
          • J Offline
            J Offline
            jrlafff
            wrote on last edited by
            #5

            I'll try the loader solution today. One question I have is: can I get a reference to one of the loaded item's child by calling findChild() on one of the loader's parent items?

            p3c0P 1 Reply Last reply
            0
            • J jrlafff

              I'll try the loader solution today. One question I have is: can I get a reference to one of the loaded item's child by calling findChild() on one of the loader's parent items?

              p3c0P Offline
              p3c0P Offline
              p3c0
              Moderators
              wrote on last edited by
              #6

              @jrlafff Yes it will work. It will work even if you use findChild over the rootObjects().

              157

              1 Reply Last reply
              0
              • J Offline
                J Offline
                jrlafff
                wrote on last edited by jrlafff
                #7

                It works indeed.

                However, the components do not appear the second time I try to create them. More precisely:

                1. Load.source = "source.qml";
                2. Create the component and set its parent (and visual parent) to the component loaded (source.qml)
                3. Load.source = "";
                4. Create the component and set its parent (and visual parent) to the component loaded (source.qml)

                I notice that, after step 3, the component previously loaded can still be found using findChild()... Which makes me think that the components are not destroyed.

                p3c0P 1 Reply Last reply
                0
                • J jrlafff

                  It works indeed.

                  However, the components do not appear the second time I try to create them. More precisely:

                  1. Load.source = "source.qml";
                  2. Create the component and set its parent (and visual parent) to the component loaded (source.qml)
                  3. Load.source = "";
                  4. Create the component and set its parent (and visual parent) to the component loaded (source.qml)

                  I notice that, after step 3, the component previously loaded can still be found using findChild()... Which makes me think that the components are not destroyed.

                  p3c0P Offline
                  p3c0P Offline
                  p3c0
                  Moderators
                  wrote on last edited by
                  #8

                  @jrlafff I think it should be some other problem. I tested something similar and it worked perfectly. destroyed signal was called as expected when Loader.source = "" was called.

                  157

                  1 Reply Last reply
                  0
                  • J Offline
                    J Offline
                    jrlafff
                    wrote on last edited by
                    #9

                    After nearly two days of investigation, I think I found a workaround.

                    I have a simple test program that reproduces what I think is a memory leak.

                    The following C++ code invokes a simple QML method several times:

                    for ( int i = 0; i < 100; i++ )
                    	QMetaObject::invokeMethod( pRoot, "testMethod" );
                    

                    The QML method in question is:

                    function testMethod() {
                    	idLoader.source = "Component.qml";
                    	idLoader.source = "";
                    }
                    

                    The Component.qml file simply contains a few items.

                    This causes the memory to increase, each time the code is executed.

                    By simply changing the ConnectionType to Qt::QueuedConnection (Qt::AutoConnection is the default and the caller lives in the same thread so it uses a DirectConnection by default), the behavior is totally different: the memory increases at the first call, and then stays steady, no matter how many times I run the code below:

                    for ( int i = 0; i < 100; i++ )
                    	QMetaObject::invokeMethod( pRoot, "testMethod", Qt::QueuedConnection );
                    

                    This looks like a bug to me, or a race condition causing the JavaScript garbage collector to miss items...

                    So it seems that:

                    1. Loading components dynamically from the C++ code leaks out;
                    2. Loading them in QML but calling the methods using a DirectConnection leaks out as well.

                    Anyway, I will try to re-implement the loading algorithm in my application to use QML-side loading with QueuedConnection, and check whether the memory leak is indeed fixed.

                    p3c0P 1 Reply Last reply
                    0
                    • J jrlafff

                      After nearly two days of investigation, I think I found a workaround.

                      I have a simple test program that reproduces what I think is a memory leak.

                      The following C++ code invokes a simple QML method several times:

                      for ( int i = 0; i < 100; i++ )
                      	QMetaObject::invokeMethod( pRoot, "testMethod" );
                      

                      The QML method in question is:

                      function testMethod() {
                      	idLoader.source = "Component.qml";
                      	idLoader.source = "";
                      }
                      

                      The Component.qml file simply contains a few items.

                      This causes the memory to increase, each time the code is executed.

                      By simply changing the ConnectionType to Qt::QueuedConnection (Qt::AutoConnection is the default and the caller lives in the same thread so it uses a DirectConnection by default), the behavior is totally different: the memory increases at the first call, and then stays steady, no matter how many times I run the code below:

                      for ( int i = 0; i < 100; i++ )
                      	QMetaObject::invokeMethod( pRoot, "testMethod", Qt::QueuedConnection );
                      

                      This looks like a bug to me, or a race condition causing the JavaScript garbage collector to miss items...

                      So it seems that:

                      1. Loading components dynamically from the C++ code leaks out;
                      2. Loading them in QML but calling the methods using a DirectConnection leaks out as well.

                      Anyway, I will try to re-implement the loading algorithm in my application to use QML-side loading with QueuedConnection, and check whether the memory leak is indeed fixed.

                      p3c0P Offline
                      p3c0P Offline
                      p3c0
                      Moderators
                      wrote on last edited by
                      #10

                      @jrlafff Good find indeed. I would suggest you to ask the above question on the Qt Interest Mailing List. Most often Qt Engineers answer them.

                      157

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved