Memory Leak with AssetManager

Hello, I am relatively new to Java and Jme3. I recently took on a project of making a 3D trivia game. At this point I would be nearly done, but I found a major problem: My game keeps on taking up more and more memory each time a new level loads. I believe this is called a memory leak.

Let me explain how my game works. At the start of the game, 30 “room” classes are generated. The room constructor takes in data for the rooms text, and a reference to its spatials (The reference can be derived from the “room event” string). As each floor is generated, the old rooms’ removeSelf() method is called and the new rooms’ attachSelf() method is called. I had anticipated that by doing this I could keep memory usage the same throughout the play through, but when I run the game the memory usage increases each time the player goes up a level.

Here is the code for my room class (minus some irrelevant methods and fields):

[java]

public class Room
{

public Room(String event, String scenario, String question, String solution, String correction, Boolean scene, Main app)
{

    this.app = app;
    
    numberOfRooms++;
    roomID = "Room" + numberOfRooms + "!";
    
    this.event = event;
    this.scenario = scenario;
    this.question = question;
    this.solution = solution;
    this.correction = correction;
    this.scene = scene;

    
    used = false;
    inUse = false;
               
}




    
public void attachSelf(float givenX, float givenZ)
{
    this.x = givenX;
    this.z = givenZ;
    

    if ( scene )
    {

        roomScene = app.getAssetManager().loadModel("Models/Rooms/" + event + " Scene/" + event + " Scene.j3o");
        //my models are named to be accessible given the room event
        roomScene.setLocalScale(1.35f);
        roomScene.setName("Scenery!");
        roomScene.setLocalTranslation(22f * x, 2f, -22f * z);
        sceneShape = CollisionShapeFactory.createMeshShape((Node) roomScene);
        roomControl = new RigidBodyControl(sceneShape, 0);
        roomScene.addControl(roomControl);     
        app.getBulletAppState().getPhysicsSpace().add(roomControl);
        app.getCollidables().attachChild(roomScene);
    
    }
    

    if (!event.isEmpty())
    {
   
        centerObject = app.getAssetManager().loadModel("Models/Rooms/" + event + "/" + event + ".j3o");
        centerObject.setLocalScale(1.35f);
        centerObject.setName(roomID);
        centerObject.setLocalTranslation(22f * x, 2f, -22f * z);
        centerObjectShape = CollisionShapeFactory.createMeshShape((Node) centerObject);
        centerObjectControl = new RigidBodyControl(centerObjectShape, 0);
        centerObject.addControl(centerObjectControl);
        app.getBulletAppState().getPhysicsSpace().add(centerObjectControl);
        app.getCollidables().attachChild(centerObject);
    
    }
    
   
    
}

public void removeSelf()
{
    if ( roomScene != null )
    {
        roomScene.removeControl(roomControl);
        app.getBulletAppState().getPhysicsSpace().remove(roomControl);
        app.getCollidables().detachChild(roomScene);
        roomScene = null;          
        roomControl.destroy();
        sceneShape = null;
    }
    
    if ( centerObject != null )
    {
        centerObject.removeControl(centerObjectControl);
        app.getBulletAppState().getPhysicsSpace().remove(centerObjectControl);
        app.getCollidables().detachChild(centerObject);
        centerObject = null;
        centerObjectControl.destroy();
        centerObjectShape = null;
    }
    
    event = null;
    scenario = null;
    roomID = null;
    app = null;
    
    
    
}

}

[/java]

I am hoping that someone with a better understanding of my own program will be able to look at these two methods and figure out where the references aren’t getting removed. I have read in another topic that the issue was with Assetmanager, that it had to be passed in to the constructor, but I didn’t understand the best way to do this. Any reply will be appreciated. This game of mine is due in 3 weeks and I am at a point where I may very well be at your guys’ mercy.

Do you ever call?
http://hub.jmonkeyengine.org/javadoc/com/jme3/asset/DesktopAssetManager.html#clearCache()

Have you tried running it in the memory profiler to see what is hanging around and what is holding references to it?

I will go ahead and attempt to use that method right now. I am assuming I should call it in my main program structure between calling removeself() and attachself() on all the rooms. Correct me if I am wrong.

I attempted running the memory profiler thing but, being the novice programmer I am, I had absolutely no idea what I was doing. Is it possible for you to briefly explain how I can do this, or give me a website that does so? Thank you for your response, it is a relief in itself.

You are incredible, kind sir. That method must have done the trick. I toyed around with it, and when I put in my Main app (extends simple application) before each generateNewLevel() is called, it worked. I managed to complete the first four levels without models failing to load, something I had not been able to do earlier.

This is fine as is, but I am still trying to optimize this. Let me give a basic rundown of what happened when I ran my game:

Upon loading the first level memory hit 340,000k (I’m looking at windows task manager). As the level progresses memory rises some, because models get loaded in game as well.

Upon loading the second level memory hit 600,000k.

Before loading the third level, memory was at around 730,000k. I was expecting loading the third level to increase this to 900,000k or so. Instead, it dropped down to 600,000k again. This repeated with the fourth level.

Is this how the garbage collector or whatever works? Is this what you would have expected by me using the method? Should I try to sort of use this method everywhere (in each room, or maybe even in simpleUpdate) to continuously reduce memory usage? Would doing that improve my results?

Sorry for all the questions, but as you might be able to tell I am fairly new to all this. Thank you for responding in the first place. You may have just saved my immediate surroundings.

Using a profiler is tricky business if you’ve never done it before. I’m not even sure I could properly explain it. One of the curses of having done something for so long is that it takes a lot of effort to put myself back in a “new user” mindset and I’m short of time. I used my first profiler in the mid-90s and so I was able to evolve with them as they did. There may be tutorials online as ultimately it can be an invaluable tool in the proper hands.

Anyway, clearing the cache is useful when you’ve loaded something that you don’t need anymore. I think JME is supposed to clear the cache when memory gets full but it may not be doing this as aggressively as it should be in some cases.

Clearing the cache does have a hit to performance since anything that your game could have reused it will have to reload. For example, if your levels share models and textures then they will be reloaded if the cache is cleared. That may or may not be a big deal. Entirely depends on the game.

Yes, you were witnessing the garbage collector cutting in. Your memory usage numbers may or may not be reasonable for your game. I think so far they are entirely reasonable for a Java game in general.

Okay, thank you very much. I figure you would like to know I am a highschool student creating my first game. Help from the top dogs is an honor, it’s something I don’t get too often in sports.

@BrianMan said: Okay, thank you very much. I figure you would like to know I am a highschool student creating my first game. Help from the top dogs is an honor, it's something I don't get too often in sports.

Well, you are making a good start. Your initial post had good info and your responses have been helpful in zeroing in on the issue. You are already a step ahead of many who start this journey.

Given that, if you ever do decide you want to play with a profiler then I recommend starting on a smaller application that doesn’t use JME. At least to get an idea of what it’s telling you it will be easier if the only objects create are the ones that you create and so on.