Lemur: Container cleanup issue?

Hallo togehter,

Currently I am playing around with JME3 and Lemur running into a behavior whichI do not understand.
I started with the first tutorial showing the initialization which I have found here Getting Started · jMonkeyEngine-Contributions/Lemur Wiki · GitHub
and modified it in order to check wether the SimpleApplications guiNode object can be returned to its initial state just by removing the components added to the GuiNode.

What I have found out is that it is not possible for the garbage collector to cleanup a container if the container has components attached to it. The following example performs the initialization of a window with a label showing up. After three seconds the program performs exactly the (in my opinion) inverse operation on the guiNode scene graph. I would have expected that the label and the containers finalizer methods are called with the garbage collector being executed every cycle. But only the label seems to be removed and the container remains in heap.

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.renderer.RenderManager;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.BaseStyles;

/**

  • This is the Main Class of your Game. You should only do initialization here.

  • Move your Logic into AppStates or Controls

  • @author normenhansen
    */
    public class LemurTest extends SimpleApplication {

    public static void main(String[] args) {
    LemurTest app = new LemurTest();
    app.start();
    }

    private float time = 0f;
    private Container myWindow = null;

    @Override
    public void simpleInitApp() {

     // Initialize the globals access so that the defualt
     // components can find what they need.
     GuiGlobals.initialize(this);
    
     // Set 'glass' as the default style when not specified
     GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");
    
     BaseStyles.loadGlassStyle();
    
     // 1. initialize container
     myWindow = new Container() {
         @Override
         public void finalize() throws Throwable {
             System.out.println("Container has been removed");
         }
     };
    
     myWindow.setLocalTranslation(300, 300, 0);
    
     // 2. add children
     myWindow.addChild(new Label("Test") {
         @Override
         public void finalize() throws Throwable {
             System.out.println("Label has been removed");
         }
     });
     // 3. add container to rootNode
     guiNode.attachChild(myWindow);
    

    }

    @Override
    public void simpleUpdate(float tpf) {
    time += tpf;
    if (time > 3 && myWindow != null) {
    // inverse 3.
    guiNode.detachAllChildren();
    // inverse 2.
    myWindow.clearChildren();
    // inverse 1.
    myWindow = null;
    }
    System.gc();
    }

    @Override
    public void simpleRender(RenderManager rm
    ) {
    }
    }


Console output:
Label has been removed


How can I take back the initialization? Because it is possible that in my project I would add several containers and remove them again and again.

Best regards,
Harry

Run it in a memory debugger… there is nothing preventing the container from being GC’ed. And actually, simply detaching the container and setting myWindow to null would be enough.

A memory profiler will tell you what’s really going on, though. Could be something strange with the anonymous inner classes or something. There is nothing in Lemur that should prevent the GC.

Actually I found out that if I do not attach the Label to the container then the Container is actually cleaned up as expected.

After doing some memory profiling without a result I have altered the program once more so that the initialization and the cleanup is now performed in the update loop. Now the cleanup of the label and the container are performed as expected. Thanks again.

/**

  • This is the Main Class of your Game. You should only do initialization here.

  • Move your Logic into AppStates or Controls

  • @author normenhansen
    */
    public class LemurTest extends SimpleApplication {

    public static void main(String[] args) {
    LemurTest app = new LemurTest();
    app.start();
    }

    private Container myWindow = null;
    private int time = 0;

    @Override
    public void simpleInitApp() {

     // Initialize the globals access so that the defualt
     // components can find what they need.
     GuiGlobals.initialize(this);
     GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");
     BaseStyles.loadGlassStyle();
    

    }

    @Override
    public void simpleRender(RenderManager rm
    ) {
    }

    @Override
    public void simpleUpdate(float tpf) {
    System.out.println(time);
    time++;
    if ((time & 0x01) > 0) {
    // Do cleanup
    // inverse 3.
    guiNode.detachAllChildren();
    // inverse 2.
    if (myWindow != null) {
    myWindow.clearChildren();
    }
    // inverse 1.
    myWindow = null;

     } else {
    
         // 1. initialize container
         myWindow = new Container() {
             @Override
             public void finalize() throws Throwable {
                 System.out.println("Container has been removed");
             }
         };
         myWindow.setLocalTranslation(300, 300, 0);
    
         // 2. add children
         myWindow.addChild(new Label("Test") {
             @Override
             public void finalize() throws Throwable {
                 System.out.println("Label has been removed");
             }
         });
         // 3. add container to rootNode
         guiNode.attachChild(myWindow);
     }
     System.gc();
    

    }
    }

Result:

0
1
2
3
Label has been removed
Container has been removed
4
5
Label has been removed
Container has been removed
6
7
Label has been removed
Container has been removed
8
.
.
.

Regards,
Harry