Search this site

 Home  I/O  Buffering  Character streams  NIO intro  Buffers  Channels  Buffer performance

Search this site:
Threads Database Profiling Regular expressions Random numbers Compression Exceptions C Equivalents in Java

 What do you think of this article? Did it help you? Found a mistake? Feedback and suggestions here

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:

  • once allocated, their memory address is fixed for the lifetime of the buffer;
  • because their address is fixed, the kernel can safely access them directly and hence direct buffers can be used more efficiently in I/O operations;
  • in some cases, accessing them from Java can be more efficient (there's potentially less overhead in looking up the memory address and/or other housekeeping required before accessing a Java object)— see the example NIO buffer performance measurements made in Hotspot under Windows;
  • via the Java Native Interface, you can actually set the address arbitrarily if required (e.g. to access hardware at a particular address, or to perform the allocation yourself).

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:

  • you're creating a relatively restricted number of buffers that will be relatively long-lived;
  • performance is crucial, and you're reasonably sure that using a direct buffer will have a performance gain (typical cases are where you use the buffer for I/O).

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

comments powered by Disqus

Written by Neil Coffey. Copyright © Javamex UK 2012. All rights reserved.