JDK NIO之ByteBuffer的局限性如下:
(1)长度固定,一旦分配完成,它的容量将不能动态扩展和收缩,而需要编码的POJO对象大雨ByteBuffer的容量时,会发生索引越界异常;
(2)只有一个标识位置的指针position,读写的是偶需要搜公条用flip()和rewind()等,使用着必须小心的处理这些API,否则很容易导致程序越界异常;
(3)ByteBuffer的API功能有限,一些高级和实用扽特性不支持,需要使用者自己编程实现、
为了弥补这些不足,Netty提供了自己的缓冲区实现ByteBuf。
ByteBuf与ByteBuf一样维护了一个byte数组,提供以下几类基本功能;
* 7中java基础类型,byte数组,ByteBuffer等的读写;
* 缓冲区自身的copy和slice等;
* 设置网络字节序;
* 构造缓冲区实例;
* 操作位置指针等方法;
ByteBuf通过两个位置指针来协助缓冲的读写操作:读指针:readerIndex和写指针writerIndex;
因为netty中ByteBuf的读写索引比较简单,这里对于读写索引的关系及相关的API不做详细的介绍,感兴趣的读者可以去相关的API参考。
一、ByteBuf与ByteBuffer的相互转换:
ByteBuf与ByteBuffer的相互转换:
@Override
public ByteBuffer nioBuffer() {
return nioBuffer(readerIndex, readableBytes());
}
nioBuffer的具体实现这里使用PooledHeapByteBuf中的实现来看:
@Override
public ByteBuffer nioBuffer(int index, int length) {
checkIndex(index, length);
index = idx(index);
ByteBuffer buf = ByteBuffer.wrap(memory, index, length);
return buf.slice();
}
一、ByteBuf的继承结构:
ByteBuf可以分为两类:
(1)对内存:HeapByteBuf自己缓冲区,特点是内存的分配和回收速度快,可以被JVM自动回收,,缺点是如果使用Socket的IO读写,需要额外做一次内存复制,将堆内存对应的额缓冲区复制到内核Channel中,性能会有一定的下降。
(2)直接内存。DirectByteBuf字节缓冲区也可以叫做直接缓冲区,非堆内存。它在堆外进行内存分配,相比于堆内存,它的分配和回收速度会慢一些。但是将它写入或者从SocketChannel中读取时,由于少了一次内存复制。速度比堆内存要快。
因此Netty提供了多种ByteBuf 的实现共开发者选择。在长期的开发实践中,表明,在IO通信线程的读写缓冲区使用DirectByteBuf, 后端业务消息的编解码模块使用HeapByteBuf,这样组合可以达到性能最优。
从内存回收的角度看,ByteBuf也分为两类:基于对象池的ByteBuf和普通ByteBuf。两者的主要区别就是基于对象池的ByteBuf可以重用ByteBuf对象,它自己创建了一个内存池,可以循环利用创建的额ByteBuf,提升内存的使用效率,降低由于高负载导致的频繁GC。测试表明使用内存池后的Netty在高负载,大并发冲击下的内存和GC更加平稳。
二、AbstractByteBuf部分源码介绍:
一、读操作:
@Override
public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
checkReadableBytes(length);
getBytes(readerIndex, dst, dstIndex, length);
readerIndex += length;
return this;
}
@Override
public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
checkDstIndex(index, length, dstIndex, dst.length);
System.arraycopy(memory, idx(index), dst, dstIndex, length);
return this;
}