A texture (or audio?) memory leak?

Hey there, I’ve run into some trouble with a memory leak in my game.
I’m not an expert at tracking down leaks, but after hours of testing in jVisualVM i’ve noticed that a bunch of buffered images (and their costly integer arrays) linger in the game without any references that I’m aware of.

My game is tile-based and has a minimap, for which I generate a few dozen textures & geometries based on the level layout. (These levels are often randomly generated, so loading from disk is not an option) There’s a different texture for each room, and they are to be recolored at various points of the game. I’m sure there’s a more efficient way of doing it, but both loading times and overall performance have been great (I have a decade old PC) so I didn’t feel the need to combine them into one texture, especially since I have no experience with drawing onto a texture on the fly.

I’ve been drawing buffered images to create these textures and then converted them into a jme image, then created a 2D texture with it. (I’d be willing to look into drawing into a jme image directly sometime in the future)

After a session ends, I’ve made sure that all the relevant spatials are removed, the references cleared, and I’ve even tried disposing of the jme images in two ways, in case they somehow kept the bufferedimages alive:

  1. The dispose function on the image, and
  2. The deleteImage function on the renderer. (I’ve also made sure to dispose of the graphics2D objects I used to draw into the bufferedImages)

Sadly, I’m out of ideas. Did I miss something? Do I need to somehow clear the texture2D objects that I create aswell?

Do you have memory issues / oom messages? The AssetManager caches the loaded data but also unloads it when new memory is needed and theres no references to the assets anymore.

Profilers can be very confusing if you don’t know how to use them or looking for a particular problem :slight_smile:

1 Like

Sorry, I forgot to mention that my game goes over the Xmx VM limit I’ve set. I’ve not actually hit an oom error yet, but I’ve managed to get the game to use 1,2gb of ram (double of what it’s supposed to be using) so loading another session ended up doing some crazy memory swapping (I have 3GB of ram)

I also run System.gc(); at the start and end of each session, and still there’s about a 100mb buildup on average for each new session I start.

Java doesn’t use more heap than you assign. If you look up the memory usage in the “task manager” of windows or something, theres also the direct memory playing into that as well as the memory the JVM uses for itself. You can set both heap and direct memory limits separately via the JVM command line options.

If java as allocated some memory once it won’t release it anymore, but that doesn’t mean that memory is actually used. The JVM memory usage in the task manager is misleading there.

You are right. My game actually never seems to go over ~3-400mb in used heap size.
I’ve tried setting “-XX:MaxDirectMemorySize=”, though it hasn’t exactly solved my problems. From what I gather it is added on top of the heap size? Specifying a relatively low direct memory size limit (256M) gives me an OOM error after several sessions, if I specify more than that my PC quickly reaches 95% ram usage, which I would not wish upon my worst enemy. :slight_smile:

I’m actually suspicious of my audio system now. I’ve completely overlooked the fact that after many sessions, sounds completely refuse to play. I didn’t notice until now because the music keeps playing fine, so I ignored it.
Feels like all the audio channels get used up but are never freed. :S
Right now I am merely removing the references to audionodes (as far as I’m aware anyway), stopping them, and have even tried:

getAudioRenderer().deleteAudioData(audioNode.getAudioData());

but it made no difference.

Also keep in mind, that bullet physics does also allocate c++ objects, these are neither direct nor heap, and can be quite huge for MeshCollisionShapes due to their internal acceleration tree (I have seen 4x due to this already). So make sure you are not leaking them as well.

For most jME3 apps, direct memory usage will be much higher than heap usage. That’s why a small heap is highly recommended to be used.
Do you get any errors when you set the heap size low? E.g. via the Xmx JVM directive.

Yes, my game seems to crash after several sessions with a low xmx value. It runs out of direct memory.
(This is without setting a custom direct memory limit)
Btw, my game doesn’t use bullet or any kind of physics at all.

I’ve managed to fix an audio leak that I’ve had, which filled up all the audiochannels. Previously, upon stopping a session, I tested all sounds to see if they’re playing, and stop them if they were:

if(getStatus() == AudioSource.Status.Playing)
stop();

I don’t pause any sound ever in my game, and dumping the status of all ingame sounds can confirm that. Somehow though, removing the “if statement” got rid of the issue I had… which would lead me to believe some sounds WERE actually playing but stating otherwise, or there are some inner workings I’m not aware of.

So after a lot of testing, it appears I need to set the direct memory limit to ~1.5 times the heap size in order for my game not to crash. (merely matching heap size definitely crashed after a number of sessions)

I’m feeling fairly good about the current state of affairs, seeing as my game will not go crazy with ram usage now and a limit of 256M/384M heap and direct memory seemed to work nicely.

Thank you all for your help! :slight_smile:

On a somewhat related note, do callables keep a reference somewhere after being invoked in the queue? I use a number of callables to do delayed tasks but never keep my own reference or call .get() on it. Could this be a potential (even if minor) leak?

There was a big rewrite to the audio engine in jME 3.1… A bug might have been fixed, or introduced, who knows … :chimpanzee_woot:

Regarding callables, they are discarded once executed, so should not leak.

In fact, I usually use 2x. There is little harm in having a direct mem limit “too high” as it doesn’t affect when GC runs. There is definitely harm in setting heap too high relative to direct mem, though.

1 Like