-DirectByteBufferのView
Grizzlyではリクエスト電文を読むところなどでnioで登場したDirectByteBufferを利用しています。
DirectByteBufferはJavaVMのヒープ外の、OSに近いところでのメモリ確保の方法です。デカいメモリブロックに高速にアクセスすることができます。しかし、じゃあ全部DirectByteBufferにしたらよいかというとそれは無い。なぜかというと、このVM外でやるメモリ確保処理の時間コストが大きいのです。Javaヒープでメモリブロックを確保するほうが時間はかかりません(と、APIドキュメントで説明されています。自分でプロファイルして確かめてませんが、これは間違いないとみてよいでしょう)。
Grizzlyはここに一時ビューのギミックを使っています。com.s..e..w..c..grizzly.ByteBufferFactory.allocateView()というメソッドではあっけないような下記ロジックです。
public synchronized static ByteBuffer allocateView(int size, boolean direct){ if (byteBuffer == null || (byteBuffer.capacity() - byteBuffer.limit() < size)){ if ( direct ) byteBuffer = ByteBuffer.allocateDirect(capacity); else byteBuffer = ByteBuffer.allocate(capacity); } byteBuffer.limit(byteBuffer.position() + size); ByteBuffer view = byteBuffer.slice(); byteBuffer.position(byteBuffer.limit()); return view; }
(DirectByteBufferに限らずにヒープ上の場合でも)大きめのバッファをはじめに確保しておいて、それを細切れに使うことによってメモリブロック確保の速度問題を回避しています。これは結構いろんなところでつかえるテクニックじゃないかな?position()やlimit()はnio.Bufferの基本的な操作メソッドです。これの詳しい説明書こうかと思ったら、グーグル先生が四次元データさんが詳しいよん、と教えてくれましたw。前後、NIOのAPIについてひととおり説明されています。
http://www.techscore.com/tech/J2SE/NIO/1-3.html
ByteBuffer#slice()で新しいByteBufferインスタンスを作っていますが背後のメモリブロックは共有していて新たにメモリ確保しているのではないというところがミソです。「byteBuffer」はstaticな変数ですが、再確保されてByteBufferFactoryが参照を失っても、一時ビューの出先でハードリンクされているでしょうからここで保持しなくていいという。このへん地味だけどスーパーな感じ。私はちょっと感動した(変?)。ただ、上記リストみて疑問なのは、capacity以上のサイズを要求したら破綻しちゃうよね。ちなみにcapacityはデフォルトで4,000,000という値になっています。3.8MBもあるからいいんですけど。。。