Deallocating Direct Buffer Native Memory in Java for JOGL

喜夏-厌秋 提交于 2019-11-27 04:39:40

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 >= 1.9, 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 (in this JEP, still a draft, probably not available in Java 1.9) is very promising, we won't need to use non public APIs.

public long memory(long index) {
    // The scope where the memory region is available
    // Implements AutoClosable but `close` can be called manually as well
    try (Scope scope = new NativeScope()) {
        // Allocate the actual memory area, in this case in the style of a "long-array"
        Pointer<Long> ptr = scope.allocate(
                    NativeLibrary.createLayout(long.class), numElements);

        // Get the reference to a certain element
        Reference<Long> ref = ptr.offset(index).deref();

        // Set a value to this element through the reference
        ref.set(Long.MAX_VALUE);

        // Read the value of an element
        return ref.get();
    }
}

N.B: sun.misc.Cleaner has been moved into jdk.internal.ref.Cleaner in Java 1.9 in the module "java.base" but the latter implements java.lang.Runnable (thanks to Alan Bateman for reminding me that difference). Then, you just have to cast the cleaner to a Runnable and to call the method run() (don't call it by reflection to avoid getting a java.lang.IllegalAccessException). It works, I've just tested (August, 6th, 2016) with Java 1.9 Early Access build 129.

However, jdk.internal.ref.Cleaner might be moved to java.ref.Cleaner$Cleanable later.

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

mikera

Direct buffers are tricky and don't have the usual garbage collection guarantees - see for more detail: http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#direct

If you are having issues, I'd suggest allocating once and re-using the buffer rather than allocating and deallocating repeatedly.

The way in which deallocation is done is awful -- a soft reference is basically inserted into a Cleaner object, which then does deallocation when owning ByteBuffer is garbage collected. But this is not really guaranteed to be called in timely manner.

Deallocating a Direct Buffer is a job done by the garbage collector some time after the ByteBuffer object is marked.

You could try calling the gc immediatly after deleting the last reference to your buffer. At least there's a chance that the memory will be free'd a bit faster.

Rather than abuse reflection of non-public apis you can do this trivially, entirely within the supported public ones.

Write some JNI which wraps malloc with NewDirectByteBuffer (remember to set the order), and an analogous function to free it.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!