How to garbage collect a direct buffer in Java
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:
/**
* DirectByteBuffers are garbage collected by using a phantom reference and a
* reference queue. Every once a while, the JVM checks the reference queue and
* cleans the DirectByteBuffers. However, as this doesn't happen
* immediately after discarding all references to a DirectByteBuffer, it's
* easy to OutOfMemoryError yourself using DirectByteBuffers. This function
* explicitly calls the Cleaner method of a DirectByteBuffer.
*
* @param toBeDestroyed
* The DirectByteBuffer that will be "cleaned". Utilizes reflection.
*
*/
public static void destroyDirectByteBuffer(ByteBuffer toBeDestroyed)
throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException, SecurityException, NoSuchMethodException {
Preconditions.checkArgument(toBeDestroyed.isDirect(),
"toBeDestroyed isn't direct!");
Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner");
cleanerMethod.setAccessible(true);
Object cleaner = cleanerMethod.invoke(toBeDestroyed);
Method cleanMethod = cleaner.getClass().getMethod("clean");
cleanMethod.setAccessible(true);
cleanMethod.invoke(cleaner);
}
The ByteBuffer
documentation says:
A direct byte buffer may be created by invoking the
allocateDirect
factory method of this class. The buffers returned by this method typically have somewhat higher allocation and deallocation costs than non-direct buffers. The contents of direct buffers may reside outside of the normal garbage-collected heap, and so their impact upon the memory footprint of an application might not be obvious. It is therefore recommended that direct buffers be allocated primarily for large, long-lived buffers that are subject to the underlying system's native I/O operations. In general it is best to allocate direct buffers only when they yield a measureable gain in program performance.
In particular, the statement "may reside outside of the normal garbage-collected heap" seems relevant to your example.
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.
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.