Netty-BetyBuf

ByteBuf 是 Netty 中的数据交互单位,本质是一个 Byte 数组的缓冲区,有不同实现机制,首先看 ByteBuf 的数据结构

ByteBuf结构

+-------------------+------------------+------------------+
| discardable bytes |  readable bytes  |  writable bytes  |
|                   |     (CONTENT)    |                  |
+-------------------+------------------+------------------+
|                   |                  |                  |
0      <=      readerIndex   <=   writerIndex    <=    capacity

ByteBuf 包括三部分,丢弃字节、可读字节、可写字节

通过两个指针,读指针(readerIndex)和写指针(writerIndex)来分成三部分,当 readerIndex = writerIndex 时不可读,当 writerIndex = capacity 时不可写。还有个参数 maxCapacity,当写入数据容量不足时会自动扩容,扩容的最大容量为 maxCapacity 值

常用API

容量API

  • capacity()

    表示 ByteBuf 的占用字节内存,包括丢弃字节、可读字节、可写字节,不同的底层实现机制有不同的计算方式

  • maxCapacity()

    表示 ByteBuf 最大能够占用多少字节的内存

  • readableBytes()

    ByteBuf 当前可读的字节数,它的值等于 writerIndex-readerIndex

  • isReadable()

    返回是否可读,writerIndex = readerIndex 则不可读,返回 false

  • writableBytes()

    ByteBuf 当前可写的字节数,它的值等于 capacity-writerIndex

  • isWritable()

    返回是否可写,capacity = writerIndex 则不可写

  • maxWritableBytes()

    ByteBuf 可写的最大字节数,它的值等于 maxCapacity-writerIndex

指针相关API

  • readerIndex()

    返回当前读指针的 readerIndex

  • readerIndex(int)

    设置读指针

  • writeIndex()

    返回当前写指针的 writerIndex

  • writeIndex(int)

    设置读指针

  • markReaderIndex()markWriterIndex()

    把当前的读、写指针保存起来

  • resetReaderIndex()resetWriterIndex()

    把当前的读、写指针恢复到之前保存的值

读写API

  • writeBytes(byte[] src)

    把字节数组 src 里面的数据全部写到 ByteBuf,src 字节数组大小的长度通常小于等于 writableBytes()

  • readBytes(byte[] dst)

    把 ByteBuf 里面的数据全部读取到 dst,这里 dst 字节数组的大小通常等于 readableBytes()

  • writeByte(byte b)

    表示往 ByteBuf 中写一个字节,类似还有 writeBoolean()writeChar()writeShort()writeInt()writeLong()writeFloat()writeDouble()

  • readByte()

    表示从 ByteBuf 中读取一个字节,类似还有 readBoolean()readChar()readShort()readInt()readLong()readFloat()readDouble()

  • setBytes()setByte()

    writeBytes() 等方法类似,但是 set 不会改变读写指针,而 write 会改变写指针

  • getBytesgetByte()

    readBytes() 等方法类似,同样 get 不会改变读写指针,而 read 会改变读指针

  • retain()

    将 BetyBuf 的引用计数加一

  • release()

    将 ByteBuf 的引用计数减一,减完之后如果发现引用计数为0,则直接回收 ByteBuf 底层的内存

由于 Netty 使用了堆外内存,而堆外内存是不被 jvm 直接管理的,也就是说申请到的内存无法被垃圾回收器直接回收,所以需要我们手动回收。有点类似于c语言里面,申请到的内存必须手工释放,否则会造成内存泄漏。

Netty 的 ByteBuf 是通过引用计数的方式管理的,如果一个 ByteBuf 没有地方被引用到,需要回收底层内存。默认情况下,当创建完一个 ByteBuf,它的引用为1

  • slice()

    从原始 ByteBuf 中截取一段,这段数据是从 readerIndex 到 writeIndex,同时,返回的新的 ByteBuf 的最大容量 maxCapacity 为原始 ByteBuf 的 readableBytes();底层内存以及引用计数与原始的 ByteBuf 共享

  • duplicate()

    把整个 ByteBuf 都截取出来,包括所有的指针信息。底层内存以及引用计数与原始的 ByteBuf 共享

  • copy()

    从原始的 ByteBuf 中拷贝所有的信息,包括读写指针以及底层对应的数据,底层内存以及引用计数都独立,操作 ByteBuf 中的数据不会影响到原始的 ByteBuf

三个方法都会返回新的 ByteBuf 对象

slice()duplicate() 的相同点是:底层内存以及引用计数与原始的 ByteBuf 共享,也就是说经过 slice() 或者 duplicate() 返回的 ByteBuf 调用 write 系列方法都会影响到原始的 ByteBuf,但是它们都维持着与原始 ByteBuf 相同的内存引用计数和不同的读写指针

slice()duplicate() 的不同点是:slice() 只截取从 readerIndex 到 writerIndex 之间的数据,它返回的 ByteBuf 的最大容量被限制到 原始 ByteBuf 的 readableBytes(), 而 duplicate() 是把整个 ByteBuf 都与原始的 ByteBuf 共享

slice()duplicate() 不会改变 ByteBuf 的引用计数,所以原始的 ByteBuf 调用 release() 之后发现引用计数为零,就开始释放内存,调用这两个方法返回的 ByteBuf 也会被释放,这个时候如果再对它们进行读写,就会报错。因此,我们可以通过调用一次 retain() 方法 来增加引用,表示它们对应的底层的内存多了一次引用,引用计数为2,在释放内存的时候,需要调用两次 release() 方法,将引用计数降到零,才会释放内存

slice()duplicate()copy() 三个方法均维护着自己的读写指针,与原始的 ByteBuf 的读写指针无关,相互之间不受影响

  • retainedSlice()

    截取内存片段的同时,增加内存的引用计数,等价于 slice().retain()

  • retainedDuplicate()

    也是截取内存片段的同时,增加内存的引用计数,等价于 duplicate().retain()

分享到: