Direct ByteBuffers are excellent when you can make use of a long-lived, fixed-size buffer that you are using to communicate with a native resource (e.g. socket, file, etc.)
My own experience with using direct ByteBuffers is allocating read/write buffers to a running Redis process that I use to write commends to the server and read the results back. The difference in performance using a direct buffer instead of raw byte[] (basically a standard ByteBuffer) were astounding.
I have seen people argue against the use of direct buffers pointing out that at some point your calls and payload have to cross the JVM-native barrier and using a direct ByteBuffer simply moves the point of entry/exit which won't change the performance of the entire round-trip.
I can't argue with that, but I would point out that in my own work with Redis, having a native process input and output data to and from a native OS buffer that I can then pull into the JVM gave me at least an order of magnitude improvement in speed than sticking with raw byte[] in and out over a socket.
ASIDE: I attribute my success here to the fact that I was able to queue up out-bound commands as fast as possible inside the JVM, pushing them out into the native buffer space which streamed them into Redis; reading back the replies as quickly as possible in a separate thread. My understanding is that by moving the "blood-brain-barrier" to this point, I am allowing Redis to consume and produce as fast as possible as long as I keep the input buffer full and output buffer relatively empty. In other words Redis wasn't being blocked (for the most part) by waiting on me to push and pull data in and out of my running JVM on every single read/write.
ADDENDUM: Just had a fun impl thought for anyone that read this and thought it was interesting... a custom InputStream and OutputStream impl along the lines of the JDK's Buffered streams, but the input and output streams are actually backed by direct ByteBuffers.
The use-cases for the stream would need to be very specific and the underlying approach clearly spelled out in the Javadoc, but it would provide a nice bridge between standard JDK stream-based I/O and the NIO work without burdening the caller with knowing about how to use the NIO APIs.
For anyone interesting, I'll likely add a first-pass impl of this to the Universal Binary JSON Java libs[1] later today to compliment the re-usable ByteArray stream impls that are there already.
[1] https://github.com/thebuzzmedia/universal-binary-json-java