ByteBuf
本质上是一个字节数组,但根据存放位置不同,它有几种不同的操作模式。
数据存储在JVM的堆空间内。此时可以直接操作数据。
ByteBuf buf = ...// get ByteBuf
if(buf.hasArray()){
//ByteBuf in heap
byte[] array = buf.array();
int length = buf.readableBytes();//get readable length
int offset = buf.arrayOffset()+buf.readerIndex();
handleArray(array,offset,length);//your method
}
数据存储在JVM的堆外内存。可以避免每次调用本地I/O前都将缓冲区的内容复制到一个临时的缓冲区。ByteBuffer
类采用了此种实现,其Javadoc指出:
直接缓冲区的内容将会驻留在常规的会被垃圾回收的堆之外。
不过,相比于堆内内存而言,其分配和释放代价较大。而且,使用时必须复制到堆内。
ByteBuf buf = ...// get ByteBuf
int length = buf.readableBytes();//get readable length
if(!buf.hasArray()){
//ByteBuf in heap
byte[] array = buf.array()
int offset = buf.arrayOffset()+buf.readerIndex();
handleArray(array,offset,length);//your method
}
复合缓冲区提供了多个ByteBuf的一个聚合视图。在Netty中主要是通过CompositeByteBuf
类实现的。它提供了将多个缓冲区表示为一个缓冲区的抽象。
在同时含有堆上和堆外内存的ByteBuf时,对
CompositeByteBuf
类调用hasArray()
方法会返回false。
例如,考虑一个由两部分组成的消息:HTTP消息,它由头部和主体构成,而这两部分由不同的模块生成。因此,每个消息都需要重新构建头部,但其主体可能是可为多个消息复用的。 以下展示了构建CompositeByteBuf的复合缓冲区模式
//构建CompositeByteBuf
CompositeByteBuf buf = Unpooled.compositeBuffer();
ByteBuf headBuf = ...//get from backing or direct
ByteBuf bodyBuf = ...//get from backing or direct
buf.addComponents(headBuf,bodyBuf);
...//do something
buf.removeComponents(0);//remove the index 0 ByteBuf(the first component)
for(var message:buf){
System.out.println(buf.toString());
}
由于可能不能直接访问其原始数组,因此在使用上类似于直接缓冲区模式。以下展示了访问CompositeByteBuf
数据的操作。
//访问CompositeByteBuf
CompositeByteBuf compBuf = ...//get CompositeByteBuf
int length = compBuf.readableBytes();
byte[] array = new byte[length];
compBuf.getBytes(compBuf.readerIndex(),array);// copy CompositeByteBuf to new array
handleArray(compBuf,0,array.length);//your method
在套接字的I/O操作中,Netty也使用了CompositeByteBuf
来优化由于JDK缓冲区所带来的性能及内存使用率的惩罚。