netty从入门到放弃--信息传输载体ByteBuf

1 ByteBuf的数据结构

从上一篇的文章中可以看到,最终发送消息或者接收到消息时,都是一个ByteBuf对象,这一篇我们来探索下这个载体类。

ByteBuf数据结构
ByteBuf是一个数组结构,可以根据下标进行读写操作,也可以进行重复读写,这个完美的解决了传统Stream不可重复读写的问题。

  1. 从上图从左到右分别是已废弃区域、可读区域、可写区域以及可扩容区域;已废弃字节区域是已经读过,无效的区域;可读区域是ByteBuf数据的主要区域,这个区域里是未读取的数据,后续读取数据都是从这里读取出来的;可写字节区域是后续写入数据时的区域,接下来需要写入的数据都会写到这个区域来;最后一部分是可扩容的区域,当ByteBufcapacity部分用完后,可继续扩容并且写入到这部分区域中。
  2. 从readIndex开始是读取指针位置,数据从这部分开始读取,每读取一个字节,readIndex就加1,所以可读容量为,writeIndex - readIndex, 当readIndex == writeIndex时,不可再往下读;
  3. writeIndex表示当前写数据的位置,每写一个字节,writeIndex就加1,直到writeIndex为capacity,调用isWritable()方法时,返回false
  4. 另外图中最上面的变量是maxCapacity,是指最大容量,即当writerIndex == capacity时,还可以扩充的容量,直到写入的字节数为maxCapacity的值

ByteBuf实际上是一个抽象类,是载体类的抽象,只是定义了使用的api,下面来学习下一些常用的api

2 ByteBuf获取以及回收

默认获取方式

// 初始化bytebuf 初始容量 8,最大容量 30
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(8, 30);

ByteBufAllocator

// 这里也可以可以使用这两种方法获取ByteBufAllocator
ByteBufAllocator allocator = new UnpooledByteBufAllocator(true);
ByteBufAllocator allocator = new PooledByteBufAllocator(true);

true跟false的区别仅在于buffer(),如果是调用heapBuffer(),那还是堆内存,跟true/false无关

堆内存获取方式

ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer();

一般情况下我们采用默认的方式获取ByteBuf,如果要显式的获取堆内内存或者使用堆外内存的分配方式,可使用后面两种方式获取。

由于netty默认使用堆外内存,这部分内存不被JVM所管理,GC时也是无法回收这部分内存的,这部分内存需要进行手动管理,有点类似于C/C++语言中内存使用完毕后由程序员进行回收释放,否则会引发内存泄漏;
ByteBuf使用引用计数方式,当创建一个ByteBuf时,它的引用次数为1,当使用完成时,需要调用它的

release()

方法,每调用一次引用减1,直到引用次数为0,则ByteBuf使用的内存会被回收;
假如需要增加它的引用计数,则调用

retain()

3 容量api

capacity()

表示 ByteBuf 底层占用了多少字节的内存(包括丢弃的字节、可读字节、可写字节)

maxCapacity()

表示 ByteBuf 底层最大能够占用多少字节的内存,当向 ByteBuf 中写数据的时候,如果发现容量不足,则进行扩容,直到扩容到 maxCapacity,超过这个数,就抛异常

readableBytes() 

readableBytes() 表示 ByteBuf 当前可读的字节数, 计算方式为:readableBytes = writerIndex - readerIndex,如果writerIndex等于readerIndex,则表示数据已经读取完毕,不可再读

isReadable()

当 readableBytes() 大于0,则返回true,表示可读,否则返回false,表示不能不可读了

writableBytes()maxWritableBytes()

writableBytes()表示当前可写字节数,它的值为 capacity - writerIndex, 当capacity等于writerIndex时,表示不可在写入 ;此时如果maxCapacity > capacity的值时,还可以扩容写入,直到writerIndex的值等于maxCapacity

isWritable()

当writerIndex=capacity时,isWritable() 返回false,但是这时并不代表不可写了,如果maxCapacity>capacity,再次调用write…()写入时,capacity 的值为 maxCapacity,并且isWritable()返回true,直到writerIndex等于maxCapacity。

容量代码实践

ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(5, 8);
byteBuf.writeInt(4);
show(byteBuf, "writeInt(4)");
byteBuf.readInt();
show(byteBuf, "readInt()");
byteBuf.writeByte(1);
show(byteBuf, "writeByte(1)");
byteBuf.writeShort(2);
show(byteBuf, "writeShort(2)");

4 读写指针

readerIndex()readerIndex(int)

前者表示获取当前读取的下标,后者表示设置当前读下标

markReaderIndex()  
resetReaderIndex()

前者表示标记(保存)读下标,后者为设置当前读下标为markReaderIndex()的值

writerIndex()
writerIndex(int)

markWriterIndex()
resetWriterIndex()

同read的用法

读写下标代码实践

byteBuf.writeShort(2);
byteBuf.markWriterIndex();
show(byteBuf, "writeShort(2)");
byteBuf.writeShort(4);
byteBuf.markWriterIndex();
show(byteBuf, "writeShort(4)");
byteBuf.writeByte(1);
show(byteBuf, "writeByte(1)");
byteBuf.resetWriterIndex();
show(byteBuf, "resetWriterIndex()");

// 读下标方法同上

5 读写api

ByteBuf读写的api常用的有 readByte()、 writeByte(), 此外类似的还有readShort()、writeShort(), readInt()、writeInt(),readLong()、writeLong(), readBoolean()、writeBoolean()等等方法

readBytes(byte[] src)writeBytes(byte[] dst)

writeBytes() 表示把字节数组 src 里面的数据全部写到 ByteBuf,而 readBytes() 指的是把 ByteBuf 里面的数据全部读取到 dst,这里 dst 字节数组的大小通常等于 readableBytes(),而 src 字节数组大小的长度通常小于等于 writableBytes(),以上方法都会影响读写下标
此外还提供了各种get、set方法

ByteBuf.getXXX()
ByteBuf.setXXX()

getXXX(),setXXX()方法都只是读取或者修改数据,不会修改下标

接下来学习下ByteBuf常用的几个"复制"的方法

slice()duplicate()copy()

slice()方法默认会从原始的ByteBuf中截图原始ByteBuf中readableBytes长度的ByteBuf,即"复制"后的ByteBuf的maxCapacity为原始ByteBuf的readableBytes,当然slice()也可以指定截取下标和长度;slice()方法的"复制"流程如下
slice

duplicate()方法会"复制"整个原始ByteBuf的数据,相当于slice(0, byteBuf.maxCapacity()), 并且包括了原始ByteBuf的下标信息;"复制"过程如下
duplicate

copy()方法就如其名字,会复制原始ByteBuf的所有容量以及原始ByteBuf中readableBytes的数据,不包括指针信息,复制的流程如下
copy

三个方法比较,共同点:
1 三个方法都会从原ByteBuf复制一段内容;
2 三个复制后的ByteBuf下标移动,不会影响ByteBuf的下标;

不同点:
1 slice()和duplicate() 中的数据修改,会影响到原ByteBuf的数据
2 slice() 方法与 duplicate() 方法不会拷贝数据,它们只是通过改变读写指针来改变读写的行为,而最后一个方法 copy() 会直接从原始的 ByteBuf 中拷贝所有的信息,包括读写指针以及底层对应的数据,因此,往 copy() 返回的 ByteBuf 中写数据不会影响到原始的 ByteBuf
3 slice()和duplicate() 与原ByteBuf公用引用计数,而copy是一个新的ByteBuf对象,拥有自己的引用计数,所以在使用copy后一定记得调用release()方法释放内存

代码地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值