I have a memory leak that I have isolated to incorrectly disposed direct byte buffers.
ByteBuffer buff = ByteBuffer.allocateDirect(7777777)
The GC collects the objects that harbor these buffers but does not dispose of the buffer itself. If I instantiate enough of the transient objects containing buffers I get this encouraging message.
java.lang.OutOfMemoryError: Direct buffer memory
I have been searching up this problem and apparently
buff.clear
and
System.gc()
does not work
The DBB will be deallocated once it hits the reference queue, and the finalizer is run. However, as we cannot depend on a finalizer to run, we can use reflection to manually call its "cleaner".
Using reflection:
The allocated memory is realized through a native libary. This memory will be freed when the ByteBuffer#finalize method is called, iaw when the Buffer is gc'd. Have a look at the allocate() and finalize() implementations of DirectByteBufferImpl.
buff.clear()
is not necessary,System.gc()
will only help if, like others already mentioned, there's no more reference left to the ByteBuffer object.The
ByteBuffer
documentation says:In particular, the statement "may reside outside of the normal garbage-collected heap" seems relevant to your example.
Here is a refined implementation that will work for any direct buffer:
As long as you are relying on sun (oracle) specific implementation, a better choice than trying to change the visibility of java.nio.DirectByteBuffer is to use the sun.nio.ch.DirectBuffer interface via reflections.
I suspect that somewhere your application has a reference to the ByteBuffer instance(s) and that is preventing it from being garbage collected.
The buffer memory for a direct ByteBuffer is allocated outside of the normal heap (so that the GC doesn't move it!!). However, the ByteBuffer API provides no method for explicitly disposing of / deallocating a buffer. So I assume that the garbage collector will do it ... once it determines that the ByteBuffer object is no longer referenced.