Freeing a direct NIO buffer with CLinker.freeMemory() causes a double free or corruption

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Freeing a direct NIO buffer with CLinker.freeMemory() causes a double free or corruption

gouessej
Administrator
Hello

I have to release the memory allocated on the native heap manually in T.U.E.R when going from a level to another one in order to manage this heap in a timely manner, i.e without relying on garbage collection.

However, calling CLinker.freeMemory() on a direct NIO buffer used to store texture data stops the program:
[java] AVERTISSEMENT: Attempting to update a texture that is not currently on the card.
     [java] double free or corruption (!prev)

BUILD FAILED
/home/gouessej/Documents/programmation/java/workspace/tuer/pre_beta/build.xml:430: Java returned: 134

Am I doing something completely stupid? By the way, I use OpenJDK 17. I'll try OpenJDK 19 as soon as possible.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: Freeing a direct NIO buffer with CLinker.freeMemory() causes a double free or corruption

gouessej
Administrator
I've just found a solution alone but it requires some more invasive changes (Java >= 19):
- create a memory session
- create a native memory segment with this memory session
- create a direct NIO buffer with this memory segment
- use the direct NIO buffer
- close the memory session when I no longer need to use the direct NIO buffer
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: Freeing a direct NIO buffer with CLinker.freeMemory() causes a double free or corruption

Sven Gothel
Administrator
Julien, what is 'CLinker.freeMemory()' ?
Reply | Threaded
Open this post in threaded view
|

Re: Freeing a direct NIO buffer with CLinker.freeMemory() causes a double free or corruption

gouessej
Administrator
Hello Sven

Sorry, the documentation of this method is here:
https://docs.oracle.com/en/java/javase/17/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/CLinker.html#freeMemory(jdk.incubator.foreign.MemoryAddress)

I used this method to free the allocated memory of a direct NIO buffer in a more straightforward manner than by relying on non public fields and methods. The use case is quite common in games, there are some appropriate moments to determine whether you still need some resources and when they are no longer useful (when you leave a level), you don't want to keep them, which isn't a problem when you deal only with the Java heap most of the time (the garbage collector G1 works well and you can use a Low-Pause-Time garbage collector like Shenandoah) but it's trickier when you deal with the native heap. I have used for years some very complicated source code to clean the mess. The method above was intended to allow to clean this mess a lot more easily by allowing to wrap a direct NIO buffer into some objects of a more recent API only when you need to free the native memory. However, it wasn't working correctly.

When I use Java 19, I have to use java.lang.foreign to create the direct NIO buffer, not only to destroy them. It requires much more changes. I'll probably have to add a flexible mechanism into JogAmp's Ardor3D Continuation to allow to override the build-in helper that creates the direct NIO buffer. I don't want to add a strong dependency to a preview API into the core of the engine.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: Freeing a direct NIO buffer with CLinker.freeMemory() causes a double free or corruption

Sven Gothel
Administrator
Thank you, now I get it.
(first I was like, Tuer uses OpenCL?)

Then we shall earmark this NIO memory management API to be included in GlueGen using reflection,
i.e. make it available when available :)

Do we see more and more convergence with C++ here? :) /jk
Funny, it was this manual memory management and implied potential potholes
which (politically) lead NIST to shame C++ last quarter 2022.
A long debate which I referenced here.
Reply | Threaded
Open this post in threaded view
|

Re: Freeing a direct NIO buffer with CLinker.freeMemory() causes a double free or corruption

gouessej
Administrator
I don't plan to use OpenCL yet, an OpenGL compute shader would be enough to perform some computations on the GPU.

Yes, such management could be done in com.jogamp.common.nio.Buffers.newDirectByteBuffer(final int numElements) {
    return nativeOrder(directByteBufferAllocator.apply(Integer.valueOf(numElements)));
}

private static Function<Integer, ByteBuffer> directByteBufferAllocator = ByteBuffer::allocateDirect;

public static void setDirectByteBufferAllocator(final Function<Integer, ByteBuffer> directByteBufferAllocator) {
    Buffers.directByteBufferAllocator = Objects.requireNonNull(directByteBufferAllocator);
}

Another solution would consist of duplicating many calls to accept java.lang.foreign.MemorySegment instead of java.nio.Buffer but it's too early to do that.
Julien Gouesse | Personal blog | Website