我们在进行数据传输的时候经常用的缓冲区,学习ByteBuf前我们先看下java NIO自带的ByteBuffer。
第一:ByteBuffer源码分析
ByteBuffer内部字段
byte[] buff 即内部用于缓存的数组。
position 当前读取的位置。
mark 为某一读过的位置做标记,便于某些时候回退到该位置。
capacity 初始化时候的容量。
limit 读写的上限,limit<=capacity。
put 写模式下,往buffer里写一个字节,并把postion移动一位。写模式下,一般limit与capacity相等。
flip 写完数据,需要开始读的时候,将postion复位到0,并将limit设为当前postion。
get 从buffer里读一个字节,并把postion移动一位。上限是limit,即写入数据的最后位置。
clear 将position置为0,并不清除buffer内容。
类图
ByteBuffer存在几大缺点
1:ByteBuffer长度固定,一旦分配完成,它的容量不能所以动态扩展和收缩,常常引发索引越界异常。如:BufferOverflowException
2:ByteBuffer只有一个标识位置的指针position,读写的时候需要手动调用flip()和rewind()等,使用者需要谨慎处理这些API。
3:ByteBuffer的API功能有限,一些高级和使用的特性他不支持,需要开发者手动编程实现。
第二:ByteBuf源码分析
1:ByteBuf实现类
ByteBuf提供了一些较为丰富的实现类,逻辑上主要分为两种:HeapByteBuf和DirectByteBuf,实现机制则分为两种:PooledByteBuf和UnpooledByteBuf,除了这些之外,Netty还实现了一些衍生ByteBuf(DerivedByteBuf),如:ReadOnlyByteBuf、DuplicatedByteBuf以及SlicedByteBuf。
类图结构
HeapByteBuf和DirectByteBuf区别在于Buffer的管理方式:HeapByteBuf由Heap管理,Heap是Java堆的意思,内部实现直接采用byte[] array;DirectByteBuf使用是堆外内存,Direct应是采用Direct I/O之意,内部实现使用java.nio.DirectByteBuffoer。
2、简要的ByteBuf的实现机制
ByteBuf有两个指针,readerIndex和writerIndex,用以控制buffer数组的读写。读逻辑较为简单,不考虑边界的情况下,就是`return array[readerIndex++];`。这里简要分析一下HeapByteBuf的读逻辑。
1. AbstractByteBuf.ensureWritable(minWritableBytes);
2. calculateNewCapacity(writerIndex + minWritableBytes)
> 2.1 判断是否超过可写入容量 maxCapacity – writerIndex
> 2.2 超过则抛异常,否则计算新容量 writerIndex + minWritableBytes
> 2.3 判断是否超过设定阈值(4MB),超过每次增加按阈值(4MB)递增,否则
> 2.4 初始大小为64字节(newCapacity),新容量超过newCapacity则翻倍,直到newCapacity大于新容量为止
> 2.5 返回Min(newCapacity, maxCapacity);
3. UnpooledHeapByteBuf.capacity(newCapacity);
> 3.1 确保可访问,有一个`引用计数`的机制,引用计数为0,则抛异常(ensureAccessible)
> 3.2 常规操作:判断是否越界
> 3.3 如果newCapacity比原容量大,则直接创建新数组,并设置。否则
> 3.4 如果readerIndex小于新容量,将readable bytes拷贝至新的数组,反之将readerIndex和writerIndex均设置为newCapacity。
4. setByte(writerIndex++, value)
> 4.1 确保可访问
> 4.2 设置