[Solved] Input Streams for Loading Assets

I’m trying to fix a memory leak and it looks like the problem may be the way I’m loading in my models. I’ve been using assetManager.loadModel(“model.j3o”) to load my objects. I ran a test case with a single node, no physics and no LODs, and when I clear all my references to this node as well as any of it children, the memory still does not clear up.

I tried assetManager.clearCache() and assetManager.deleteFromCache() but that doesn’t seem to work; if I clear the cache this way, the memory usage actually goes up for some reason. I looked around and it looks like using assetManager.LoadAssetFromStreamI() is supposed to bypass the cache, but I haven’t found any working examples and I can’t seem to get this to work myself. Could anyone enlighten me as to how I can get this working? Thanks

1 Like

What do you mean by “memory leak?” Is this memory that your OS reports the JVM process is using?

1 Like

I’m having trouble cleaning up my direct memory usage for my models, which I’m tracking with the jme Memory Utils class. When the Node is loaded into my game my direct memory usage goes up to about 800. When I detach the node and return to the menu the DM stays at 800, and if I play again without closing the application it will never go above 800 and also loads the node immediately, which makes me think the model is still being held onto by the asset manager.

1 Like

What happens if you try to force a GC cycle after detaching the node? I believe that the asset manager is using a weak reference cache, so if the GC hasn’t run it won’t be reclaimed.

1 Like

I forgot to put the System.gc() in my test case, but I added it and it’s doing the same. The memory usage is 0 mb with nothing attached, and goes up to 65mb and never goes back down even with this following test case. Maybe I’m just very msiguided as to how to properly clean up an object :sweat_smile: but here’s the code I thought would do the trick

   private Node n;


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

@Override
public void simpleInitApp() {   
    
    /** A white, directional light source */ 
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun); 


    n = (Node)assetManager.loadModel("test.j3o");
    rootNode.attachChild(n);
}

private float interval = 2;
private float time = 4;
@Override
public void simpleUpdate(float tpf) {
       time -= tpf;
       if(interval < 0){
            float mb = MemoryUtils.getDirectMemoryUsage() / 1000000;
            System.out.println("Mem: " + mb + "mb");
            interval = .5f;
  }
  interval -= tpf;

  
  if(time < 0 && n != null){
      rootNode.detachChild(n);
      n = null;
      System.gc();
  }
}

@Override
public void simpleRender(RenderManager rm) {
    //TODO: add render code
}

}

1 Like

At this point I’m going to have to defer to someone more knowledgeable about the asset manager cache and how direct memory is handled - this is outside of the realm of what I’ve dealt with. This may even be expected behaviour to keep future loads fast.

2 Likes

I appreciate the help regardless :slight_smile: and ya I’m thinking you may be right, this type of asset caching is definitely optimal for not having to constantly reload any low memory objects such as enemies or projectiles that de-spawn and re-spawn frequently. But for unique map tiles and terrains which take up a whole lot of memory there has to be a way around this.

1 Like

No problem at all. :slight_smile:

1 Like

Not my area of expertise but

SimpleAssetCache is an asset cache that caches assets without any automatic removal policy. The user is expected to manually call deleteFromCache(com.jme3.asset.AssetKey) to delete any assets.
(http://javadoc.jmonkeyengine.org/com/jme3/asset/cache/SimpleAssetCache.html)

The preferred cache class for this asset type. Specify “null” if caching is to be disabled. By default the SimpleAssetCache is returned.
(http://javadoc.jmonkeyengine.org/com/jme3/asset/AssetKey.html#getCacheType--)

I’m assuming this is what’s going on. I don’t actually know how to set an asset to use a different cache type like this one:

This implementation will remove the asset from the cache once the asset is no longer referenced in user code and memory is low, e.g. the VM feels like purging the weak references for that asset.
(http://javadoc.jmonkeyengine.org/com/jme3/asset/cache/WeakRefAssetCache.html)

I’ve just stuck with deleteFromCache when and as I need.

2 Likes

Interesting, do you use the AssetKey object or ModelKey object? I tried using a ModelKey and it seems to cause some of my map tiles to not reload after the cache is cleared. I also tried an AssetKey but i don’t think I converted my asset to a j3o correctly when I did so, the scene just showed up blank but I’ll have to give it a try again with an AssetKey instead of ModelKey

1 Like

Honestly I’ve only ever used TextureKey, my only dealings with this has been keeping a strict control of textures since I have loads of them at different times.

I just read your original post again, sorry I missed some of that. So did you do node.getKey() and then assetManager.deleteFromCache() after using loadModel() when it didn’t work?

2 Likes

Ya I just tried that and it seems to give me the same results :frowning: does this look I did what you suggested properly?

public class Main extends SimpleApplication {

private Node n;

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

@Override
public void simpleInitApp() {   
    /** A white, directional light source */ 
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun); 
    
    n = (Node) assetManager.loadModel("test.j3o");
    rootNode.attachChild(n);


}

private float interval = 2;
private float time = 8;
@Override
public void simpleUpdate(float tpf) {
    time -= tpf;
       if(interval < 0){
    float mb = MemoryUtils.getDirectMemoryUsage() ;
     System.out.println("Mem: " + mb/ 1000000f + "mb" );
     interval = .5f;
     System.gc();
  }
  interval -= tpf;

  
  if(time < 0 && n != null){
      rootNode.detachChild(n);
      assetManager.deleteFromCache(n.getKey());
      n = null;
      
      System.gc();
      
      
  }
}

@Override
public void simpleRender(RenderManager rm) {
    //TODO: add render code
}

}

1 Like

Yep : /

Uh… ok well. I tried it myself. If I call System.gc() every single frame (it didn’t work doing it only once after the model was deleted from the cache) I can get the memory to go from

Mem: 1.492498mb
to
Mem: 1.47229mb

Upon deleting it from the cache and setting it to null.

Without ever loading the model it prints out:

Mem: 0.606757mb

So I assume it isn’t working. Not sure why I’m afraid.

2 Likes

Darn, thanks for testing it out though at least I know it’s nothing else I’m messing up. I guess I can still keep building my world, I’ll just have to only run the application with a few tiles at a time and test small segments until someone can help me figure out how to get the input stream working :confused:

1 Like

I’ve heard the garbage cleaner is pretty temperamental, maybe it’ll start working better when there’s loads of stuff? I tested it with whatever model I had at hand, which was pretty small.

2 Likes

I didn’t think of that, I just tried using node.getKey() back in my main project that was near 1000 dm and it worked! It eventually got back down to near 150. Thanks a bunch :grinning:

2 Likes

Sweet, thats a relief since I never actually checked it was working with all my textureskeys :stuck_out_tongue:

1 Like

Hi @yaRnMcDonuts,
could you please tell me what exactly you did in order to solve that problem?
Best regards
Domenic

1 Like

Yeah of course, I still have no clue about input streams :laughing: but I got this to work with @JESTERRRRRR’s suggestion. It looks as if the ModelKey and AssetKey object don’t work if you instantiate them as a new objects, doing so caused real high memory spikes for me and still didn’t clear the cache at all, so i had to load and unload a model like this

 private Node n;

 private void load(){
   n = (Node) assetManager.loadModel("test.j3o");
    rootNode.attachChild(n);
 }

 private void unload(){
  rootNode.detachChild(n);
       assetManager.deleteFromCache(n.getKey());
      n = null;
      System.gc()
 }

It looks like you can safely and successfully reference a loaded model’s asset key for removal with the Node.getKey(); method. I can post anymore code if you’d need, I also think for LOD controls you need to call control.detachAndCleanUpControl();, I had to for terrain at least. I hope that helps with what you were asking, if not let me know

3 Likes

Thank you @yaRnMcDonuts that probably will help me a lot! Thanks! :slight_smile:

1 Like