Direct buffers

NIO supports a type of ByteBuffer usually known as a direct buffer. Direct buffers can essentially be used like any other ByteBuffer (and are implemented as a ByteBuffer subclass), but have the property that their underlying memory is allocated outside the Java heap. More specifically, direct buffers have the following properties:

In practice, in current versions of Hotspot, the memory is allocated via a malloc(), although this could vary in other VMs or in a future version of Hotspot.

How to create and use a direct ByteBuffer

To create a direct buffer from Java, simply call the following:

ByteBuffer directBuf = ByteBuffer.allocateDirect(noBytes);

Then the ByteBuffer returned can be used essentially like any other byte buffer. For example, all of the various get() and put methods will work, as well as methods to create views of the buffer. One thing you can't do, at least in Hotspot, is call array()— there's no Java array underlying a direct buffer, but simply a "raw" section of memory. (Though strictly, according to the Javadoc, implementations are actually free to implement a direct buffer with a backing array if they can find a way to do so...)

How do you deallocate/destroy a direct buffer?

There's actually no explicit method you can call from Java to destroy or deallocate a direct buffer. When you allocate a direct buffer, the VM effectively registers a "cleanup" method with the garbage collector which will should be called at some point once the ByteBuffer object itself is no longer reachable (informally, when it is "ready to be garbage collected")1, in order to deallocate the underlying memory that was reserved for that buffer.

Usually, this "automatic" deallocation works well enough. But it's important to bear in mind that there's no immediate link between the last time you access a direct buffer and the point at which the memory is actually deallocated.

Limits on direct buffers

When you allocate a direct buffer, you are generally impinging on whatever other parts of the process might have used malloc() to allocate memory, notably memory allocated from any native libraries used by your program.

Under some circumstances, you may wish to limit the amount of memory space that your Java application can use to allocate direct buffers. To do so, set the sun.nio.MaxDirectMemorySize property to the required limit in bytes when you start the VM. Attempting to allocate over this limit will safely throw an OutOfMemoryError. Thus, you can ensure that your Java application fails "gracefully" if it tries to allocate too much memory for direct buffers, rather than having a knock-on effect on other native code that may not be able to fail quite so gracefully if a malloc() fails (though of course, any native code you write should always try to fail gracefully if allocation fails!).

When to use a direct buffer?

In general, direct buffers are best suited to cases where:

1. See the sun.misc.Cleaner class for more details of the mechanism.

If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.