GPU memory leak, Texture(M)

Hello I know there is plenty of topic speaking about memory leak but they all come from 2009 and 2010,

At the moment I made a nice looking game, with some models and with many terrain.
But I came accros a huge problem, I only became aware of it when I was playing on login and out of a map in my game.

The textures seem to stay in the Texture(M) and then never get removed. I’ve been looking for an answer,
and I ve tryed to set null all the model before removing the reference so the garbage collector can pass but nothing did the trick…

I am certain some texture are stuck in the memory but sometime some texture are removed, but not the one stuck.
I can tell because when I detach the whole scene (the number stay for ever). When I reload 2 time the terrain it became unplayable(when it reach 250+ texture stuck).

If anyone as an idea how i can force the texture out it would be awesome.
If I have to reload the texture I will!

Got the same problem, and @nehon told me that I’m doing it wrong.

Instead of loading/unloading Textures, spritesheet/atlas should be loaded once and reused.

Does a System.gc help? If not they are not collectible.
Problem is a 4gb direct buffer is for the jvm only a few bytes in size, as the rest is not on the heap.

In some of the older threads I proposed a different bufferutils, that work more similar to a custom malloc, by only giving out subbuffers from a initially created large buffer. Benefit is that these are collected accuratly, but it has it’s own issues.

2 Likes

Well… even if it does, you are suggesting to “code bad and let the gc clean stuff for you”. :stuck_out_tongue:

Which might work and save time if you are in a hurry and want to code quickly and dirty. But these are the kind of things that make the garbage collector sad and the performance bad, expecially on mobile.

In your case that was the issue, but I can’t tell about @n3cr0’s issue, we don’t have much details.

No i mean that if System.gc does not help, it you creating the memory leak and not jme.

Also System.gc is not at all that bad in game contexts, games are not business software.
Changing level, and just finsihed loading? do a gc, so it wont be done while in the level, also the loading quite likely filled the heap up with now unnecessary objects.

Yes, but I bet my left fin the the root cause is the same. There definitely is a memory issue with assetManager.loadTexture(), at least on master.

As i described, it is not really an issue with jme, but a bug/feature in the jvm gv algorithms itself, they simply don’t understand that the object is larger than expected.
A pretty crude but working fix is, to simply add 100k byte[] to the nativeobject classes, suddenly the gc feels way more inclined to clean them up. (That solution work surprisingly great on desktop)

Another way is to manually dispose the objects, jme does offer the native cleaner method, just be careful to not shoot yourself with it, as it is undefined what happens if cleaned buffers are used.
http://javadoc.jmonkeyengine.org/com/jme3/util/NativeObject.html#dispose()

3 Likes

But unless you are generating textures at runtime, I agree with the subtext of this thread that it’s exceedingly rare to need to ‘unload’ textures.

Withing that/aside from that, everything empire says here is true, of course.

2 Likes

@n3cr0 is it possible to have a test case? We are talking about direct memory allocation and their bugs, but we don’t have sure if that’s your case.

I will reply correctly to everyone,

I already use copy of the asset, I load then with a class and i copy then all around as needed.

I’ve tried to set null everything and to force the garbage collector but the memory seem to still be referenced. It s like if they are stock on the opengl side.

I will try but the test case can be really long to make, i have to generate a lot of memory.

I made my own drawer inside my game to load and unload unnecessary object. At the beginning I had a hard time to make something efficient, then I found a way to load everything with the help of the concurrency(multi threading). The first way I used was to load tile of model and to generate then as needed. But I didn’t find the algorithm really fast so I decided to make some use of the java memory and to copy all the map directly into the ram with all there models.
The maximum memory usage would be 5 maps per 5 maps, with each 128x128 tile of models.(max possible,it s never the maximum) Each maps are 129x129.(everything is loaded in concurrency during game-play, the map is infinite).
The ammount stock seem to be 13

I am telling all this to give information if anyone find I did something really wrong here just tell me.

What do you mean by generating texture? I think I am generating texture when i load the terrain! :open_mouth:

Well, I don’t know what terrain library you are using… but JME loads terrain as a mesh normally, not a texture.

If System.gc() didn’t clear them then there is still a hard reference somewhere… and the memory profiler can tell you where.

I build the terrain alpha map(for the terrainLighting) 2 time.

    buffImg1 = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
    buffImg2 = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
    
    for(int i = 0; i < size ; i++){
        for(int j = 0; j < size ; j++){
                buffImg1.setRGB(i, j, colorMap1[i][j]);
                buffImg2.setRGB(i, j, colorMap2[i][j]);
        }
    }
    Texture2D tex1 = generateTextureMap(buffImg1);
    Texture2D tex2 = generateTextureMap(buffImg2);

Try:

        if (_manager instanceof DesktopAssetManager)
        {
            DesktopAssetManager d = (DesktopAssetManager)_manager;
            d.clearCache();
        }

I’m doing it every time I need to load different scenery or completely purge loaded items.

Yeah, ok so you are generating your textures. (Note: you are doing it with double the memory required by using BufferedImage instead of ImageRaster…)

I will change the BufferedImage for the ImageRaster but does it mean i have to manually destroy the texture from the memory?

It doesn’t make a difference either way… the buffered image was being copied into direct memory as it was or you wouldn’t have been able to use it on the GPU.

So, if you have to manually free them now then you would have before also.

BUT (and it’s a big BUT!), if System.gc() did not free these buffers then that means SOMETHING IS HOLDING A REFERENCE TO THEM. Bottom line. It has to be. It cannot be otherwise.

…and if something is holding a reference to them then manually freeing them can be EXTREMELY DANGEROUS.

You might want to learn to use the memory profiler to see what is holding onto them. If clearing the asset manager cache and System.gc() didn’t free them then something is definitely holding onto them. 100% without a doubt.

I ve removed everything but the terrain and it seem it is the terrain…