Netty源码解析(五)之ByteBuf

Netty的内存分配和内存管理可以说是Netty最复杂的内容,涉及到很多操作系统和JDK的底层知识,一篇文章肯定讲不完,那么这篇文章就先简单地讲一讲ByteBuf。

ByteBuf是一段连续且支持随机访问的存储字节的序列,在Netty中ByteBuf是一个抽象类,他提供了对字节序列的一些基本的操作。

一. ByteBuf的数据格式

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

ByteBuf内部有三个指针:readerIndex,writeIndex和capacity。

  1. readerIndex : 每次通过读操作读取一个字节后,readerIndex都会+1,最大为writerIndex。
  2. writerIndex :每次通过写操作写一个字节后,writerIndex都会+1,最大为capacity。
  3. capacity :ByteBuf的最大容量,readerIndex或writerIndex到达capacity后,如果继续进行读或写操作,则会抛出IndexOutOfBoundsException异常。

这三个指针将整个ByteBuf分为了三个区:

  1.  [0, readerIndex) : 这一部分为discardable bytes,包含了已经通过读操作读过的数据。
  2.  [readerIndex, wirterIndex) : 这一部分为readable bytes,包含还可以读的数据。
  3. [writerIndex, capacity) : 这一部分为writable bytes,是一段未被定义的区域,表示如果继续向ByteBuf写数据,就会写在这个区域。

同时,ByteBuf支持他的实现类具有扩容功能,因此还提供了一个maxCapacity,表示capacity能扩容到的最大值。

二. ByteBuf的基本操作

  • public ByteBuf clear():将ByteBuf的readerIndex和wirterIndex都初始化为0。该操作仅设置这两个指针的值,而不会真正地清除掉ByteBuf的数据。
  • public ByteBuf discardReadBytes():丢弃掉discardable bytes的数据,将readable bytes的数据从[readerIndex, wirterIndex) 移动到 [0, wirterIndex-readerIndex),并将readerIndex设置为0,wirterIndex设置为wirterIndex-readerIndex。这样就实现了对writable bytes区域的扩容。
 *  BEFORE discardReadBytes()
 *
 *      +-------------------+------------------+------------------+
 *      | discardable bytes |  readable bytes  |  writable bytes  |
 *      +-------------------+------------------+------------------+
 *      |                   |                  |                  |
 *      0      <=      readerIndex   <=   writerIndex    <=    capacity
 *
 *
 *  AFTER discardReadBytes()
 *
 *      +------------------+--------------------------------------+
 *      |  readable bytes  |    writable bytes (got more space)   |
 *      +------------------+--------------------------------------+
 *      |                  |                                      |
 * readerIndex (0) <= writerIndex (decreased)        <=        capacity
  • public int readInt():从readerIndex的位置开始读取一个int整数(4个byte),读完之后readerIndex会+4,同理还有readLong,readBytes,readDouble,readFloat等
  • public ByteBuf writeInt(int value):从writable bytes位置开始写一个int整数(4个byte),写完之后writerIndex会+4,同理也有writerLong,writeBytes等
  • public abstract ByteBuf copy():返回该ByteBuf的readable bytes的一个拷贝,该方法不会改变该ByteBuf的readerIndex和writerIndex的值。新返回的ByteBuf与老的ByteBuf是完全独立的,对其中一个ByteBuf的任何操作都不会影响另一个。
  • public ByteBuf slice():返回该ByteBuf的readable bytes的一个切片,该方法不会改变该ByteBuf的readerIndex和writerIndex的值。新返回的ByteBuf与老的ByteBuf是共享一块存储空间,只是他们的readerIndex和writerIndex是互相独立的,对其中一个ByteBuf的操作都可能会影响另一个。

三. ByteBuf的分类

ByteBuf有三种分类方法:

  1. Pooled和Unpooled。PooledByteBuf中所用到的存储空间是从一块预先分配好的内存中取出来的,而UnpooledByteBuf的存储空间是一块新的内存。因此,如果涉及到频繁的ByteBuf的申请和释放操作,应该尽量使用PooledByteBuf,会大大减少重新申请内存的时间。
  2. Unsafe和非Unsafe。UnsafeByteBuf指的是可以通过JDK的Unsafe类直接对内存进行操作,相对于非Unsafe来说,UnsafeByteBuf的读写操作会更快,但这需要JDK平台的支持。
  3. Heap和Direct。HeapByteBuf的存储空间是在Java虚拟机的堆上,通常来说就是一个byte数组(byte[]),因此它是受JVM管理的。而DirectByteBuf的存储空间是一块堆外内存,堆外内存直接受操作系统的管理,而不是JVM,因此需要手动释放。

四. ByteBufAllocator

当我们需要使用一个ByteBuf时,一般不会直接去new一个,而是会用ByteBufAllocator,ByteBufAllocator提供了申请各种ByteBuf的方法,例如:

//new一个PooledByteBufAllocator
PooledByteBufAllocator allocator = new PooledByteBufAllocator();
//申请一个initialCapacity=256,maxCapacity=512的DirectByteBuf
ByteBuf directBuffer = allocator.directBuffer(256,512);
directBuffer.writeInt(100);
System.out.println(directBuffer.readInt()); //100
//释放该ByteBuf
directBuffer.release(); 
//申请一个initialCapacity=256,maxCapacity=512的HeapByteBuf
ByteBuf heapBuffer = allocator.heapBuffer(256,512);
heapBuffer.writeInt(200);
System.out.println(heapBuffer.readInt()); //200

猜你喜欢

转载自blog.csdn.net/benjam1n77/article/details/123115520
今日推荐