GPU memory leak, Texture(M)

…if only there were some way to view the object references and no for sure what it was being held by. It could maybe give the ‘profile’ of the running application and it’s memory… Maybe someone could call it a “profiler”… :wink:

I get it I get it xD i will use a profiler

I ve tryed the profiler but nothing interesting show up about the leak.

I ve tryed plenty of thing to correct the leak and nothing seem to show up. The number of texeture in the
graphic memory seem to almost never go down. When I load a menu it add the texture once in the memory but he never remove then. Although he only add them once, so opening and closing never add more texture to the graphic memory.

The real problem comme when i load infinite map or reaload the scene on the same application runtime.

The memory seem to stay.( I did try to stop using the TerrainLighting Material and it did change something. I only get a texture stock once in a while instead of 12 texture. My Visual Object doesn’t seem to generate any stock memory texture. (so it seem only the terrain do, and the TerrainLightning do way more then the Terrain material.

But the question is if i want to save cpu time and load the texture once and i use a reference like the code below, does it cause the memory leak? (i am always keeping a reference on the assetManager Textutre so i never have to load then back. (but i do a copy of the texture to add then to the terrain, which mean i do not reference to the the texture from there anymore)

public class TerrainTexture implements Cloneable {

    private Texture dirt1, dirt2, rock, mossyStone, leaf, moss, sand, grass, road2, road3, volcanic, volcanicNormal, volcanicNormalParallax, gravel, farmSoil;
    private Texture swamp_grass1, swamp_grass2, swamp_grass3, flower, lakeRock;
    private Texture tempTexture, lakeRockNormal;

    public void terrainTexture(AssetManager assetManager) {
        int textQuality = 5;
        if (textQuality == 5) {
            
            dirt1 = assetManager.loadTexture("Textures/terrainHigh/Soil1.jpg");
            dirt2 = assetManager.loadTexture("Textures/terrainHigh/Soil2.jpg");
            grass = assetManager.loadTexture("Textures/terrainHigh/HighQualityGrass3.jpg");
            mossyStone = assetManager.loadTexture("Textures/terrainLow/Mossy1.jpg");
            rock = assetManager.loadTexture("Textures/terrainHigh/Rock.jpg");
            farmSoil = assetManager.loadTexture("Textures/terrainHigh/FarmSoil.jpg");

            volcanic = assetManager.loadTexture("Textures/BrickWall/BrickWall.jpg");
            volcanicNormal = assetManager.loadTexture("Textures/BrickWall/BrickWall_normal.jpg");
            volcanicNormalParallax = assetManager.loadTexture("Textures/BrickWall/BrickWall_height.jpg");

            swamp_grass1 = assetManager.loadTexture("Textures/terrainHigh/swamp_grass1.jpg");
            swamp_grass2 = assetManager.loadTexture("Textures/terrainHigh/swamp_grass2.jpg");
            swamp_grass3 = assetManager.loadTexture("Textures/terrainHigh/swamp_grass3.jpg");
            
            lakeRockNormal = assetManager.loadTexture("Textures/terrainHigh/50text/188_norm.JPG");
            lakeRock = assetManager.loadTexture("Textures/terrainHigh/50text/188.JPG");
            flower = assetManager.loadTexture("Textures/terrainHigh/Flower.jpg");
            
        } else {
            dirt1 = assetManager.loadTexture("Textures/terrainLow/Soil1.jpg");
            grass = assetManager.loadTexture("Textures/terrainLow/Grass1.jpg");
            mossyStone = assetManager.loadTexture("Textures/terrainLow/Mossy1.jpg");
            rock = assetManager.loadTexture("Textures/terrainLow/Rock1.jpg");
            farmSoil = assetManager.loadTexture("Textures/terrainLow/Farm1.jpg");

            volcanic = assetManager.loadTexture("Textures/BrickWall/BrickWall.jpg");
            volcanicNormal = assetManager.loadTexture("Textures/BrickWall/BrickWall_normal.jpg");
            volcanicNormalParallax = assetManager.loadTexture("Textures/BrickWall/BrickWall_height.jpg");

            swamp_grass1 = assetManager.loadTexture("Textures/terrainHigh/swamp_grass1.jpg");
            swamp_grass2 = assetManager.loadTexture("Textures/terrainHigh/swamp_grass2.jpg");
            swamp_grass3 = assetManager.loadTexture("Textures/terrainHigh/swamp_grass3.jpg");
            
            
            lakeRockNormal = assetManager.loadTexture("Textures/terrainHigh/50text/188_norm.JPG");
            lakeRock = assetManager.loadTexture("Textures/terrainHigh/50text/188.JPG");
            flower = assetManager.loadTexture("Textures/terrainHigh/Flower.jpg");
        }
    }

    public Texture getDirt1() {
        return this.dirt1;
    }
    public Texture getDirt2() {
        return this.dirt2;
    }
    
    public Texture getFarmSoil() {
        return this.farmSoil;
    }

    public Texture getRock() {
        return this.rock;
    }

    public Texture getGrass() {
        return this.grass;
    }

    public Texture getLeaf() {
        return this.leaf;
    }

    public Texture getMosse() {
        return this.moss;
    }

    public Texture getSand() {
        return this.sand;
    }

    public Texture getMossyStone() {
        return this.mossyStone;
    }

    public Texture getRoad2() {
        return this.road2;
    }

    public Texture getRoad3() {
        return this.road3;
    }

    public Texture getVolcanic() {
        return this.volcanic;
    }

    public Texture getVolcanicNormal() {
        return this.volcanicNormal;
    }

    public Texture getVolcanicNormalParallax() {
        return this.volcanicNormalParallax;
    }

    public Texture getGravel() {
        return this.gravel;
    }

    public Texture getTempTexture() {
        return this.tempTexture;
    }

    public Texture getSwampGrass1() {
        return this.swamp_grass1;
    }

    public Texture getSwampGrass2() {
        return this.swamp_grass2;
    }

    public Texture getSwampGrass3() {
        return this.swamp_grass3;
    }
    
    public Texture getFlower() {
        return this.flower;
    }
    
    public Texture getLakeRock() {
        return this.lakeRock;
    }
    public Texture getLakeRockNormal() {
        return this.lakeRockNormal;
    }
    

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

}

Well, if you hold a reference to TerrainTexture then of course none of that will ever be garbage collected.

…but you are pretty much duplicating what the AssetManager is already doing with its own cache, I guess. Every time you call:
dirt1 = assetManager.loadTexture(“Textures/terrainHigh/Soil1.jpg”);

…it’s just cloning the first Soil1.jpg texture that was loaded.

If nothing is holding a reference to the “leaked” data then it is not really being leaked and you should examine why you think that it is to make sure that’s valid. The profiler cannot really lie.

Like i said nothing seem to leak on the side of the JVM, but on the gpu part it s seem to be the case, Every time i reload the game it keep every texture loaded, I m trying hard to find the problem inside my code.

Btw i did this because it was faster then calling the AssetManager, and at first the assetManager wasn’t working in Concurency, So i had to clone the resource to acces them, then i just realise i could use synchronise .

But now i keep a reference to the resource in this case…(still i dont think this is the problem, i m setting the reference to null and nothing seem to change on the graphic card(all the node are removed, the game cant even run again unless i call load texture) and the Texture M are still loaded. And when i look at my graphic card memory it say 96%/100% loaded

By the way thank you for the time spent trying to find my issue.

It s hard when you didnt code the game to find this kind of problem.

I could give a try to add back the direct call to the assetManager and see if it solve the problem.

I will try this for now

Alright,
I ve did the test and it s like i thought,

Direct call to the AssetManager is extremely slow.
Second it didn’t solve the problem, it made it worst :frowning:
It s generate twice as much texture in the graphic memory.

My code:

        mat = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
        mat.setBoolean("useTriPlanarMapping", false);
        mat.setFloat("Shininess", 0.0f);
        
        mat.setTexture("AlphaMap", tex1);
        mat.setTexture("AlphaMap_1", tex2);
        
        //Texture 1
        Texture textur0 = myTexture.getRock();
        Texture textur1 = myTexture.getGrass();
        Texture textur2 = myTexture.getDirt1();
        Texture textur3 = myTexture.getSwampGrass2();
        
        //Texture 2
        Texture textur4 = myTexture.getFlower(); //RED
        Texture textur5 = myTexture.getLakeRock();    // GREEN  
        Texture normal5 = myTexture.getLakeRockNormal(); 
        Texture textur6 = myTexture.getFarmSoil(); //BLEU
        Texture textur7 = myTexture.getDirt2();//ALPHA
        //Texture parallaxMap7 = myTexture.getVolcanicNormalParallax();


//        Texture 1
        textur0.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap", textur0);
        mat.setFloat("DiffuseMap_0_scale", 128);
        
        textur1.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_1", textur1);
        mat.setFloat("DiffuseMap_1_scale", 128);
        
        textur2.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_2", textur2);
        mat.setFloat("DiffuseMap_2_scale", 128);
        
        textur3.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_3", textur3);
        mat.setFloat("DiffuseMap_3_scale", 128);
        
        //Texture 2
        textur4.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_4", textur4);
        mat.setFloat("DiffuseMap_4_scale", 128);
        
        
        textur5.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_5", textur5);
        mat.setFloat("DiffuseMap_5_scale", 64);
        
        normal5.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("NormalMap_5", normal5);
        
        
        textur6.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_6", textur6);
        mat.setFloat("DiffuseMap_6_scale", 128);
        
        textur7.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_7", textur7);
        mat.setFloat("DiffuseMap_7_scale", 128);

Code with the Direct call to AssetManager:

        mat = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
        mat.setBoolean("useTriPlanarMapping", false);
        mat.setFloat("Shininess", 0.0f);
        
        mat.setTexture("AlphaMap", tex1);
        mat.setTexture("AlphaMap_1", tex2);
        
        //Texture 1
        Texture textur0 = assetManager.loadTexture("Textures/terrainHigh/Rock.jpg");
        Texture textur1 = assetManager.loadTexture("Textures/terrainHigh/HighQualityGrass3.jpg");
        Texture textur2 = assetManager.loadTexture("Textures/terrainHigh/Soil1.jpg");
        Texture textur3 = assetManager.loadTexture("Textures/terrainHigh/swamp_grass2.jpg");
        
        //Texture 2
        Texture textur4 = assetManager.loadTexture("Textures/terrainHigh/Flower.jpg"); //RED
        Texture textur5 = assetManager.loadTexture("Textures/terrainHigh/50text/188.JPG");   // GREEN  
        Texture normal5 = assetManager.loadTexture("Textures/terrainHigh/50text/188_norm.JPG");
        Texture textur6 = assetManager.loadTexture("Textures/terrainHigh/FarmSoil.jpg"); //BLEU
        Texture textur7 = assetManager.loadTexture("Textures/terrainHigh/Soil2.jpg");//ALPHA
//        Texture parallaxMap7 = myTexture.getVolcanicNormalParallax();


//        Texture 1
        textur0.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap", textur0);
        mat.setFloat("DiffuseMap_0_scale", 128);
        
        textur1.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_1", textur1);
        mat.setFloat("DiffuseMap_1_scale", 128);
        
        textur2.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_2", textur2);
        mat.setFloat("DiffuseMap_2_scale", 128);
        
        textur3.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_3", textur3);
        mat.setFloat("DiffuseMap_3_scale", 128);
        
        //Texture 2
        textur4.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_4", textur4);
        mat.setFloat("DiffuseMap_4_scale", 128);
        
        
        textur5.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_5", textur5);
        mat.setFloat("DiffuseMap_5_scale", 64);
        
        normal5.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("NormalMap_5", normal5);
        
        
        textur6.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_6", textur6);
        mat.setFloat("DiffuseMap_6_scale", 128);
        
        textur7.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("DiffuseMap_7", textur7);
        mat.setFloat("DiffuseMap_7_scale", 128);

If you run in the debugger then you might trace into the loadTexture() call to see where it’s going wrong. A particular file should only be loaded once.

It’s possible that changing the wrap mode messes with the cache but it seems unlikely.

I can’t remember… does running System.gc() fix your memory problems?

Do you eventually get errors or are you just looking at some stat somewhere and getting worried?

I run into intense lag after a while(while running in a direction), when i reload i get the same lag.

I get the stats from an application reading the gpu stats and directly from the game stats like this

We could read 75 texture here

We could read 320 texture here, sorry for the bad spelling, You should read The memory increase to 2.6g ram

You know i was looking for anwser and I found some one talking about it here in 2012.

Is it possible the texture manager is broken if some one make something he shouldn’t have done somewhere?

Alright i think i ve found the leak…

Take a look at this picture, I dont think it s normal that every generated Terrain are keeping a thread alive even after I’ve removed them.( I detach them and remove the physic and remove reference)
Am I right? or it s part of the system?

And are those thread part of the physic engine or something else, and if you know how to close the process it would be awesome!

What version of JME are you running?

Latest Stable i think 3.0.10

If i generate all the map using concurrency, is it possible the executor is the one keeping all the reference?

update: never mind, It s always 1 to 3 active count while loading 3 maps

That’s odd, I thought the terrain system used a single executor service … If not, it probably should.

This might have been fixed in 3.1 which is why I asked about version. I do remember a conversation about something like this a while back and I sort of remember something being fixed. Since I have no personal interest in the built in terrain system I only half paid attention.

…either way, to OP, upgrading might be worth a try at some point.

Is the actual 3.1 version enought stable to upgrade, i heard i would have migrating job to do?

I generate multiple map in parallel using concurrency, would it be the reason it start multiple instance of the jme3 Terrain?

@BigBob wasn’t this the one with thre refelction hack as a workaround?