任何网络程序框架都会面临一个问题:如何提供一个高效的buffer?比如我们想写一个http server,那么就需要不断的从文件中读入数据,然后写入到socket中,如:
byte[] buf=new byte[4096];
while(file.read(buf)){
mysocket.write(buf);
}
java.nio中引入了一个重要的类:ByteBuffer,来做这件事情。(我的直觉是它应该和ACE的MessageBlock作用很像,但是后来发现接口迥异。)
ByteBuffer是一个抽象类,它有两种实现:HeapByteBuffer 和 DirectByteBuffer。java.nio.ByteBuffer.allocate(int)返回的是HeapByteBuffer,java.nio.ByteBuffer.allocateDirect(int)返回的是DirectByteBuffer。
HeapByteBuffer分配在jvm的堆(如新生代)上,和其它对象一样,由gc来扫描、回收。DirectByteBuffer则是通过底层的JNI向C Runtime Time通过malloc分配,在JVM的GC所管理的堆之外。
下面讨论HeapByteBuffer。
每个HeapByteBuffer内部有一个byte[]存储数据。这个byte[]在构造HeapByteBuffer的时候分配好,长度不会自动增长。
HeapByteBuffer内部有四个指针(offset):
capacity:内部这个byte数组的大小(byte[]的length)。
mark:相当于书签,初始值为-1。需要设置的时候mark()一下,需要跳回去的时候用reset()方法。
position:指向下一个读取/写入位置。初始值为0,读/写 数据的时候自动往后挪这个指针。
limit:初始值等于 capacity。
它们始终满足这样的关系:mark
flip操作:用在读写操作转换的时候。
limit = position;
position = 0;
mark = -1; //清理掉书签
示例用法:
buf.put(magic); // 先往buffer里面写入一个包头(packet header)
in.read(buf); // 然后从另外一个input stream中读入包体,并写入到buffer中
buf.flip(); // Flip buffer。刚才是往ByteBuffer里写数据,下面要转换成读数据。
out.write(buf); // 把整个buffer里的有效数据(包头+包体)读出来,写入output stream中。
但是调用这个方法之前一定要注意,不要多调用了一次。比如,把上面的第三行代码复制一遍,那么
buf.put(magic); // 先往buffer里面写入一个包头(packet header)
in.read(buf); // 然后从另外一个input stream中读入包体,并写入到buffer中
buf.flip(); // Flip buffer。position=0。
buf.flip(); // Flip buffer。limit=0!
out.write(buf); // 什么也不会写入。
假如让你实现一个readfile这样的函数,你会在函数的末尾调用buf.flip吗?
void readfile(File f,ByteBuffer bb){
f.read(bb);
bb.flip(); //Do it or not do it ? That’s a question。
}
你会在这个函数的接口注释那里说“我没调用flip!!!”吗?
ByteBuffer的toArray()?
有时候,想把ByteBuffer转成一个byte[],把它里面的有效数据拿出来。但是可惜它并没有一个toArray()这样的方法。于是就需要手动copy一下。
ByteBuffer bb;
byte[] contentsOnly = Arrays.copyOf( bb.array(), bb.position() );
DirectByteBuffer的接口和HeapByteBuffer完全一样,最大的区别就是内存位置不一样。如果有大量的文件需要以memory mapping的方式映射到内存中,那么DirectByteBuffer明显优于HeapByteBuffer。因为这部分内存不用被gc,所以降低了gc消耗。另外,HeapByteBuffer的缓存区无法作为操作系统direct io api的缓存区,因为它未必是按page size对齐的。
关于ByteBuffer的性能测试:
往ByteBuffer里面写Float:比较数组(new float[10000])、HeapByteBuffer、DirectByteBuffer的性能差距。
数组: 2.06 微秒
DirectByteBuffer:3.94 微秒
普通buffer: 16.88 微秒
测试环境:Java HotSpot(TM) 64-Bit Server VM (build 21.0-b17, mixed mode), windows 7 64bit。
测试代码:
private final FloatBuffer byteBuffer2 = ByteBuffer.allocate(40000).order(ByteOrder.nativeOrder())
.asFloatBuffer();
private final FloatBuffer byteBuffer = ByteBuffer.allocateDirect(40000).order(ByteOrder.nativeOrder())
.asFloatBuffer();
protected void testWrite() {
// Allow VM to compile
testWriteArray(1000);
testWriteByteBuffer(1000);
// Test for real
System.out.println(“Array buffer: ” + testWriteArray(50000) + ” us per iteration”);
System.out.println(“Direct buffer: ” + testWriteByteBuffer(50000) + ” us per iteration”);
}
protected double testWriteByteBuffer(int iterations) {
long timeNow = System.currentTimeMillis();
for (int j = 0; j for (int i = 0; i byteBuffer.put(i, 1234.5678f);
}
}
return 1000.0 * (System.currentTimeMillis() – timeNow) / iterations;
}
protected double testWriteArray(int iterations) {
long timeNow = System.currentTimeMillis();
for (int j = 0; j for (int i = 0; i byteBuffer2.put(i, 1234.5678f);
}
}
return 1000.0 * (System.currentTimeMillis() – timeNow) / iterations;
}
public BufferMark() {
}
public static void main(String[] args) {
for (int n = 0; n System.out.println(“=== round ” + n);
BufferMark app = new BufferMark();
app.testWrite();
}
}
转自:http://www.udpwork.com/item/6095.html