Netty中的ByteBuf和java提供的原生ByteBuffer比较
ByteBuf产生原因
当我们进行数据传输的时候,往往需要使用到缓冲区,常用的缓冲区就是JDK NIO类库提供的java.nio.Buffer。
7种基础类型(Boolean除外)都有自己的缓冲区实现,对于NIO编程而言,我们主要使用的是ByteBuffer。从功能角度而言,ByteBuffer完全可以满足NIO编程的需要,但是由于NIO编程的复杂性,ByteBuffer也有其局限性,它的主要缺点如下。
(1)ByteBuffer长度固定,一旦分配完成,它的容量不能动态扩展和收缩,当需要编码的对象大于ByteBuffer的容量时,会发生索引越界异常;
(2)ByteBuffer只有一个标识位置的指针position,读写的时候需要手工调用flip()和rewind()等,使用者必须小心谨慎地处理这些API,否则很容易导致程序处理失败;
(3)ByteBuffer的API功能有限,一些高级和实用的特性它不支持,需要使用者自己编程实现。
为了弥补这些不足,Netty提供了自己的ByteBuffer实现——ByteBuf。
结构
从上面这幅图可以看到,ByteBuf 是一个字节容器,容器里面的的数据分为三个部分:
- 第一个部分是已经丢弃的字节,这部分数据是无效的;
- 第二部分是可读字节,这部分数据是 ByteBuf 的主体数据, 从 ByteBuf 里面读取的数据都来自这一部分;
- 最后一部分的数据是可写字节,所有写到 ByteBuf 的数据都会写到这一段。最后一部分虚线表示的是该 ByteBuf 最多还能扩容多少容量;
ByteBuf和ByteBuffer的区别
1.扩容
ByteBuffer:ByteBuffer缓冲区的长度固定,分多了会浪费内存,分少了存放大的数据时会索引越界,所以使用ByteBuffer时,为了解决这个问题,我们一般每次put操作时,都会对可用空间进行校检,如果剩余空间不足,需要重新创建一个新的ByteBuffer,然后将旧的ByteBuffer复制到新的ByteBuffer中去。
ByteBuf:而ByteBuf则对其进行了改进,它会自动扩展,具体的做法是,写入数据时,会调用ensureWritable方法,传入我们需要写的字节长度,判断是否需要扩容:
注意:
1.当申请的新空间大于阀值时,采用每次步进4MB的方式进行扩张内存,而不是倍增,因为这会造成内存膨胀和浪费
2.而但申请的新空间小于阀值时,则以64为基数进行倍增而不是步进,因为当内存比较小的时候,倍增是可以接受的(64 -> 128 和 10Mb -> 20Mb相比)
2.位置指针
ByteBuffer:ByteBuffer中只有一个位置指针position(ByteBuf有两个),所以需要我们手动得调用flip等方法。ByteBuffer中会有三个下标,初始位置0,当前位置positon,limit位置。
ByteBuf:ByteBuf中使用两个指针,readerIndex,writerIndex来指示位置。
更多:
https://blog.****.net/ZBylant/article/details/83037421
https://blog.****.net/qq_40303781/article/details/88667858