I’m currently writing a 2D Graphics engine, and in a few occasions I seem to get memory leaks when working with textures. Here is my setup :
- Images are stored as a OpenGL Texture (no the JOGL Texture implementation) coupled with a pixel array (the array can be loaded from the texture, and at a later date the texture can be updated with the pixels)
- For a few texture rendering purposes, a single general Texture Rendering FBO is statically created, and is then used with glFramebufferTexture2D each time I want to modify a texture.
- For now, in the finalize method, I delete the specific texture linked with this Image (and the console shows that textures are deleted). I also tried to create a close function myself, and the leak is still present.
- The specific code I’m trying to run is to clear the screen and draw an image (loaded from a file in init), then gather the whole panel as a texture (using glReadPixels to get the pixels and then loading them onto a texture). This seems to be the line that causes the leak, but also just creating an image (not even loading it) on each frame (and although it is destructed at the end of the frame) still causes a leak.
- When displaying the Texture IDs that are being destructed at each frame, I get something like this :
and at this point the memory used by the programs can be in the Gigabytes…
I am currently not using the Texture object (would it make a difference ?). I wanted to know how JOGL's texture memory management worked (are the textures deallocated on glDeleteTextures ? Why do the texture indices keep increasing even though the textures are deleted ? Is there a specific way to make sure that the used memory doesn't get out of control ?)
As a closure to this post, I've found the solution :
I tried with a manual close function (without the GC-based finalize() method) and using the initally generated IntBuffer as the glDeleteTextures target… And I have no leak whatsoever and the memory consumption seems constant (higher at higher FPS, which can be expected since textures are loaded and unloaded so fast that memory has more “overlap”).
So I guess that the issue was that the GC was so slow to call the finalize method that at this point a lot of textures weren’t discarded and it looked like a leak… where it was just that tens of textures were not used and deleted too late by the GC (I bencharked a single of these textures to be about 30MB, so even as few as 30 simultaneous would be around 1GB, and given that the GC’s destructions could sometimes be of 50 at a time… A few GB is to be expected).
To be fair, I actually don’t even think that the glDeleteTextures call worked since when replacing the IntBuffer in finalize() with the original one, I got this error (I’m still curious on what it means) :
# A fatal error has been detected by the Java Runtime Environment:
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000000715475d9, pid=28448, tid=0x0000000000006d84
# JRE version: Java(TM) SE Runtime Environment (8.0_151-b12) (build 1.8.0_151-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C [nvoglv64.DLL+0xa175d9]
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
# An error report file with more information is saved as:
# D:\Programmation\Workspaces\Workspace 8 - Processing\SGLRL\hs_err_pid28448.log
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp # The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
But then, if I replace the finalize() call with a close() function, my memory consumption doesn’t get above a few hundred MB (expected for such a large number of images) and above all, DOESN’T INCRESE (which was the case even with a close() function if the glDeleteTextures() target wasn’t the actual initial IntBuffer).
Well I was monitoring the memory consumption on the Windows 10 Task Manager, which I guess includes Java heap and native heap.
In any case I'd guess that the leak could only have been occurring in native heap since both NIO Buffers and OpenGL Textures are stored in native heap, and since I'd guess that if they weren't, the GC would handle them (?).
But other than that I have no direct evidence that it is actually native heap, only that this is the only place where it could have been occurring.
Yeah I stumbled across java.sun.Cleaner, I'll try to implement it to clean up my Buffers, but although they cause a bit of memory usage, they don't seem to cause a leak (maybe they're natively deallocated in their finalize() method?)
The leak really looked like it was caused by OpenGL Textures, that are really unmanaged. My issue (the error message) could be caused by the fact that I just called the super.finalize() method of my Image object before destroying the textures (which needs access to the initial IntBuffer... does the finalize() method dereference the object's variables or something of the sort?). I'll test it out when I get back to this, a bit later on today :)
I advise you to read carefully what I wrote and quoted. The garbage collection deals with managed memory on the Java heap, an object needs to become eligible to garbage collection in order to be garbage collected. The native memory isn't managed, it's freed when the Java counterpart is garbage collected or when a cleaner (or similar mechanism) is used. If you lack of native memory before lacking of memory on the Java heap, rather free the native memory by yourself and if you use a recent version of Java (you seem to use Java 8, bad news :s), you won't need to use a cleaner. You error message might be caused by Java reading beyond the end of a buffer, you must be sure that the direct NIO buffer is no longer in use before releasing its native memory.
If I use a more recent version I won't need a cleaner ? This project isn't highly dependent on the Java Environment on which it is running so upgrading to a more recent Java version could be an option, but how would it help ? Does it have methods that help to deal with heap memory management natively ?
Yes, it's explained in my post on StackOverflow but it depends on a module currently in incubation. Don't confuse the Java heap and the native heap. You should clearly identify which direct NIO buffers are to blame, maybe JOGL already does the job when you call the appropriate method.