Behavior of AssetManager?

Hello,

I’ve got a few questions about AssetManager and the way it behaves.

  1. What does Textures (M) stand for? To be more precise: which (M)?

  2. This is actually the situation that made me ask quastion #1. As far as I understand textures should be shared between materials when they need the same texture file. AssetManager should handle this(?)
    However, in my implementation it doesn’t. I’ve got something between 0 and 300 geometries detaching & deleting their data, after a while loading their data & attaching back to the scenegraph using the same texture files, but Textures (M) is just increasing to an insane amount (say, 500, 600, …, 800, …)
    Textures however may be shared between materials when applicable (But this shouldn’t increase Textures (M),
    but again it does!!)
    Also when detaching all nodes and deleting geometry and material data Textures (M) does not reduce (just a tiny bit, by about 5-10%)

Yesterday I figured out a way to unload a texture successfully (took me weeks to find it), yet the answer is simple:

((DesktopManager)assetManager).deleteFromCache(textureKey);
texture=null;
System.gc();

Only then Textures(M) reduces to the number that it should be (at least exactly the number that it should be)
Without this I cannot be sure that there will be no memory leak and I must avoid leaks that would cause the application to crash sooner or later.
So I thought that maybe something is wrong with AssetManager and it doesn’t properly keep track of already cached data?

Your answers would be appreciated.

brgds,
Mike

1 Like

Just because something hasn’t been de-allocated yet doesn’t mean there is a memory leak. Things only start getting discarded once memory starts running low…

But still, if textures are loaded from same resource, they should not get duplicated?

Yes, but texture count is still far too high. In my situation it appears that one and the same texture is cached multiple times causing Textures (M) to rise and rise and rise… Why should a texture be loaded from the hard disk to cache if it has been cached already?
What you say makes sense of course, but it is not the answer to THAT question…

Oops, abies was faster… Oh myyy, this is a community - can’t even answer that fast :wink:

@Apollo said: ((DesktopManager)assetManager).deleteFromCache(textureKey); texture=null; System.gc();

I find the texture=null part kind of interesting. If something was holding a reference to the texture then the cache won’t ever give it up without manually deleting it.

Still, the same texture should only be loaded once… so I’m curious how you load the textures in the first place and where you are keeping a reference that needs to be set to null.

Yes, I’m keeping a reference to the texture in a class TerrainTextureInfo. The main purpose of the class below is to provide the terrain system (different from jME) with the appropriate information of the texture. Then I encountered the described problem with duplicate textures, so I added support for texture management in that class too. The class now keeps track of the number of terrain blocks using a specific texture, as well as the total number of terrain textures that should be in memory, and the Texture object itself. The TerrainTextureInfo object will be shared among TerrainBlocks using the same texture file.
The thing is, I can also load the texture when the material is created (the material isn’t stored anywhere else in a TerrainBlock class, it is only created in a method) but the problem stays. Also, removing the three lines above, for unloading the texture, brings the increasing “Texture (M)”-number problem back.
I’ve tried each and every combination I can think of but the method described is the only way for me to get reasonable numbers in “Textures (M)”.
Does it help?

PS: I know: “ArrayList is the near Vector”

[java]
// *****************************************************************************
public class TerrainTextureInfo
// *****************************************************************************
{

private int face;
private double left;
private double upper;
private double dimension;
private Texture texture;
private Vector myBlocks=new Vector();
private String textureFileName;

public static int numTextures;

// =============================================================================
public TerrainTextureInfo(String textureFileName, int face, double left,
double upper, double dimension)
// =============================================================================
{ this.face=face;
this.left=left;
this.upper=upper;
this.dimension=dimension;
this.textureFileName=textureFileName;
} // ===========================================================================

// =============================================================================
public void addBlock(TerrainBlock addMe)
// =============================================================================
{ if (!myBlocks.contains(addMe))
{ myBlocks.add(addMe);
}
} // ===========================================================================

// =============================================================================
public int getFace() {return face;}
// =============================================================================

// =============================================================================
public String getTextureFileName() {return textureFileName;}
// =============================================================================

// =============================================================================
public double getLeft() {return left;}
// =============================================================================

// =============================================================================
public double getUpper() {return upper;}
// =============================================================================

// =============================================================================
public double getDimension() {return dimension;}
// =============================================================================

// =============================================================================
public int getNumBlocks() {return myBlocks.size();}
// =============================================================================

// =============================================================================
public Texture getTexture()
// =============================================================================
{
if (texture==null)
{
texture=JmeUtility.assetManager().loadTexture(textureFileName);
numTextures++;
}

return texture;
} // ===========================================================================

// =============================================================================
public void removeBlock(TerrainBlock removeMe)
// =============================================================================
{ myBlocks.remove(removeMe);

if (myBlocks.size()==0)
{
numTextures–;

  ((DesktopAssetManager)JmeUtility.assetManager()).deleteFromCache(
     texture.getKey());
  texture=null;
  System.gc();

}

} // ===========================================================================

// =============================================================================
@Override
public String toString()
// =============================================================================
{ return “face=”+face+", left="+left+", upper="+upper+" dimension="+dimension;
} // ===========================================================================

}
[/java]

I think if you remove line 81 and leave line 83 then it will also still fix the problem. If TerrainInfo was holding a reference to the texture then it will never be available for GC and the cache will hold onto it.

Actually, I don’t know which weak reference strategy the cache uses so it may be slightly more aggressive than that, too. Might be you have to run into a low memory situation before the cache starts to give things up.

Also, when you loadTexture() even with the same file name then you will get a new texture instance each time even though they are sharing the same internal data. I don’t remember (and don’t have time to look at the moment) what the Texture(M) stat is tracking.

So I just made a couple of tests (again) at different configurations between line 81 to 84.
Leaving System.gc() there for release versions is unacceptable as it slows down the application, but it doesn’t even matter because the garbage collector will delete unnecessary items from memory anyway but maybe later.
Test A: If I then remove lines 81 & 82 and start the application Textures (M) is exploding! With a call to System.gc() every minute or so Textures (M) is still increasing by about 10% per minute (But when does it reach the top, and what will happen then? I don’t know, and I can’t be sure this is safe…)
Test B: However, leaving lines 81 & 82 in the code and doing the same procedure with System.gc() decreases Textures (M) in any case down to a defined level, which is exactly the number of textures the memory should hold.

So my conclusion is that I should actually keep line 81 & 82 and wait for the garbage collector to do the housekeeping. But if I have to keep these lines, there must be something wrong with the way jME manages textures in memory, no?