Search code examples
javamemorybufferlwjgl

What's the difference between BufferUtils and MemoryUtil? (LWJGL)


I always used MemoryUtil to store float buffers, but people seem to use BufferUtils for it:

private IntBuffer convertToIntBuffer(int[] data) {
    IntBuffer buffer = BufferUtils.createIntBuffer(data.length);        
    return buffer.put(data).flip();
}
private FloatBuffer convertToFloatBuffer(float[] data) {
    FloatBuffer buffer = MemoryUtil.memAllocFloat(data.length);     
    return buffer.put(data).flip();
}

Solution

  • LWJGL 3's org.lwjgl.BufferUtils class is only a small facade over Java's java.nio.ByteBuffer.allocateDirect() method, allowing you to use the JVM's memory allocator to allocate off-heap memory and return a NIO ByteBuffer (or typed view thereof), including making sure that the ByteOrder is nativeOrder().

    The NIO Buffer allocated by ByteBuffer.allocateDirect() is managed by the JRE internally and the native memory is freed implicitly as part of the garbage collection cycle once it becomes unreachable.

    There are a lot of downsides to allocating off-heap memory with this approach, such as (quote from "Memory management in LWJGL 3"):

    -begin-of-quote-

    • It is slow, much slower than the raw malloc() call. A lot of overhead on top of a function that is already slow.

    • It scales badly under contention.

    • It arbitrarily limits the amount of allocated memory (-XX:MaxDirectMemorySize).

    • Like Java arrays, the allocated memory is always zeroed-out. This is not necessarily bad, but having the option would be better.

    • There's no way to deallocate the allocated memory on demand (without JDK-specific reflection hacks). Instead, a reference queue is used that usually requires two GC cycles to free the native memory. This quite often leads to OOM errors under pressure.

    -end of quote-

    LWJGL 3's org.lwjgl.system.MemoryUtil class on the other hand allows you to use other native/off-heap memory allocators instead of the JVM's ByteBuffer allocator to allocate off-heap native memory, including the option of just giving you the raw virtual memory address as a long, avoiding the NIO Buffer instance.

    LWJGL supports the system allocator of the C standard library (malloc) as well as currently jemalloc and rpmalloc. All of these provide a very much faster alternative to Java's ByteBuffer.allocateDirect(), alleviating the above mentioned drawbacks.

    Because the native memory is not managed by the JVM anymore, you have to free the memory yourself, for which there is the org.lwjgl.system.MemoryUtil.memFree() method.

    Before you continue, you should however read the mentioned LWJGL blog post in its entirety, as there are more options, such as org.lwjgl.system.MemoryStack, for allocating native off-heap memory in particular situations (such as short-lived memory), which is even faster than all the other alternatives above mentioned.