5

I'm writing some stuff that uses ByteBuffers. In the docs of the API it says

There is no way to free a buffer explicitly (without JVM specific reflection). Buffer objects are subject to GC and it usually takes two GC cycles to free the off-heap memory after the buffer object becomes unreachable.

However in a SO post's accepted answer I read

BigMemory uses the memory address space of the JVM process, via direct ByteBuffers that are not subject to GC unlike other native Java objects.

Now what should I do, shall I free the created buffer? Or do I misunderstand something in the docs or the answer?

agiro
  • 2,018
  • 2
  • 30
  • 62
  • Will do. Papa Google popped up the docs first then some SO posts which caused the misunderstanding, hence the question. It is good to have that link here for me and for anyone else who might stumble upon this post. – agiro Mar 29 '19 at 13:10

2 Answers2

3

It depends how you create the buffer, there are many possible use cases. Regular ByteBuffer.allocate() will be created on the heap and will be collected by the GC. Other options e.g. native memory might not.

Terracotta BigMemory is a type of native off-heap memory which is not governed by the JVM GC. If you allocate a buffer in this type of memory you have to clear it yourself.

It might be a good idea to clear the buffer even if it's allocated in the heap memory. GC will take care of collecting unused buffer it but this will take some time.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • Thanks, but the reason for confusion was the part _via direct ByteBuffers that are not subject to GC_. So the Terracotta BM uses `ByteBuffer`s differently? Or are their same names just a coincidence? – agiro Mar 29 '19 at 13:04
3

As the documentation of the BufferUtils in LWJGL also say: There is no way to explicitly free a ByteBuffer.

The ByteBuffer objects that are allocated with the standard mechanism (namely, by directly or indirectly calling ByteBuffer#allocateDirect) are subject to GC, and will be cleaned up eventually.

The answer that you linked to seems to refer to the BigMemory library in particular. Using JNI, you can create a (direct) ByteBffer that is not handled by the GC, and where it is up to you to actually free the underlying data.


However, a short advice: When dealing with LWJGL and other libraries that rely on (direct) ByteBuffer objects for the data transfer to the native side, you should think about the usage pattern of these buffers. Particularly for OpenGL binding libraries, you'll frequently need a ByteBuffer that only has space for 16 float values, for example (e.g. containing a matrix that is sent to OpenGL). And in many cases, the methods that do the data transfer with these buffers will be called frequently.

In such a case, it is usually not a good idea to allocate these small, short-lived buffers repeatedly:

class Renderer {
    void renderMethodThatIsCalledThousandsOfTimesPerSecond() {
        ByteBuffer bb = ByteBuffer.allocateDirect(16 * 4);
        fill(bb);
        passToOpenGL(bb);
    }
}

The creation of these buffers and the GC can significantly reduce performance - and distressingly in the form of GC pauses, that could cause lags in a game.

For such cases, it can be beneficial to pull out the allocation, and re-use the buffer:

class Renderer {

    private final ByteBuffer MATRIX_BUFFER_4x4 = ByteBuffer.allocateDirect(16 * 4);

    void renderMethodThatIsCalledThousandsOfTimesPerSecond() {
        fill(MATRIX_BUFFER_4x4);
        passToOpenGL(MATRIX_BUFFER_4x4);
    }
}
Marco13
  • 53,703
  • 9
  • 80
  • 159
  • Thanks for the answer, it is highly worth mentioning for performance reasons. Right now I'm just using assimp in Java code and fear memory leaks though. – agiro Mar 29 '19 at 13:14