Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deallocating Direct Buffer Native Memory in Java for JOGL

I am using direct buffers (java.nio) to store vertex information for JOGL. These buffers are large, and they are replaced several times during the application life. The memory is not deallocated in time and I am running out of memory after a few replacements.

It seems that there is not good way to deallocate using java.nio's buffer classes. My question is this:

Is there some method in JOGL to delete Direct Buffers? I am looking into glDeleteBuffer(), but it seems like this only deletes the buffer from the video card memory.

Thanks

like image 985
sparrow400 Avatar asked Aug 16 '10 19:08

sparrow400


People also ask

What is native memory in JVM?

Native memory is the memory provided to the application process by the operating system. The memory is used for heap storage and other purposes. The native memory information available in the native memory perspective view varies by platform but typically includes the following information: Table 1.

What is direct buffer memory?

The direct buffer memory is the OS' native memory, which is used by the JVM process, not in the JVM heap. It is used by Java NIO to quickly write data to network or disk; no need to copy between JVM heap and native memory.

What is native heap in Java?

The native, or system heap, is allocated by using the underlying malloc and free mechanisms of the operating system, and is used for the underlying implementation of particular Java objects; for example: Motif objects required by AWT and Swing.

How do you create a buffer in Java?

Creating a buffer A buffer can be created by invoking one of the static methods of its subclasses. The allocate() method creates a buffer with its specified initial capacity. The wrap() method wraps an existing byte array into it and creates a buffer.


1 Answers

The direct NIO buffers use unmanaged memory. It means that they are allocated on the native heap, not on the Java heap. As a consequence, they are freed only when the JVM runs out of memory on the Java heap, not on the native heap. In other terms, it's unmanaged = it's up to you to manage them. Forcing the garbage collection is discouraged and won't solve this problem most of the time.

When you know that a direct NIO buffer has become useless for you, you have to release its native memory by using its sun.misc.Cleaner (StaxMan is right) and call clean() (except with Apache Harmony), call free() (with Apache Harmony) or use a better public API to do that (maybe in Java > 12, AutoCleaning that extends AutoCloseable?).

It's not JOGL job to do that, you can use plain Java code to do it yourself. My example is under GPL v2 and this example is under a more permissive license.

Edit.: My latest example works even with Java 1.9 and supports OpenJDK, Oracle Java, Sun Java, Apache Harmony, GNU Classpath and Android. You might have to remove some syntactical sugar to make it work with Java < 1.7 (the multi catches, the diamonds and the generics).

Reference: http://www.ibm.com/developerworks/library/j-nativememory-linux/

Direct ByteBuffer objects clean up their native buffers automatically but can only do so as part of Java heap GC — so they do not automatically respond to pressure on the native heap. GC occurs only when the Java heap becomes so full it can't service a heap-allocation request or if the Java application explicitly requests it (not recommended because it causes performance problems).

Reference: http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#direct

The contents of direct buffers may reside outside of the normal garbage-collected heap

This solution is integrated in Java 14:

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
   ...
}

You can wrap a byte buffer into a memory segment by calling MemorySegment.ofByteBuffer(ByteBuffer), get its memory address and free it (this is a restricted method) in Java 16:

CLinker.getInstance().freeMemoryRestricted(MemorySegment.ofByteBuffer(myByteBuffer).address());

Note that you still need to use reflection in many non trivial cases in order to find the buffer that can be deallocated, typically when your direct NIO buffer isn't a ByteBuffer.

N.B: sun.misc.Cleaner has been moved into jdk.internal.ref.Cleaner in Java 1.9 in the module "java.base", the latter implemented java.lang.Runnable (thanks to Alan Bateman for reminding me that difference) for a short time but it's no longer the case. You have to call sun.misc.Unsafe.invokeCleaner(), it's done in JogAmp's Gluegen. I preferred using the Cleaner as a Runnable as it avoided to rely on sun.misc.Unsafe but it doesn't work now.

My last suggestion works with Java 9, 10, 11 and 12.

My very latest example requires the use of an incubated feature (requires Java >= 14) but is very very simple.

There is a good example in Lucene under a more permissive license.

like image 51
gouessej Avatar answered Sep 20 '22 05:09

gouessej