在Netty中并无使用Java自带的ByteBuffer,而是本身实现提供了一个缓存区来用于标识一个字节序列,并帮助用户操做原始字节或者自定义的POJO。
Java NIO的ByteBuffer问题数组
如上图,channel与对端的I/O读写都要操做Buffers。当有读操做时,把数据从内核区读取到用户区,当有写操做时,把数据从用户区写到内核区。
ByteBuf是Netty的实现的最基本的数据缓冲,它包括Heap Buffer和Direct Buffer。 ByteBuf实现了高级的功能和API,是Java NIO ByteBuffer更高级的封装和实现。
如上图是ByteBuf的结构,其中:缓存
当写入N个数据后,writerIndex向后移动,但要保证writerIndex<capacity,这时可读数据为writerIndex,可写数据长度为capacity-writerIndex
每读一个数据,readerIndex向后移动,但readerIndex<=writerIndex,可读数据writerIndex-readerIndex这时readerIndex-0则是可丢弃的数据段
若是数据继续写入缓冲区,而缓存区的writable区间已经被判断空间不足,那就有两个方法能够解决:
动态扩展缓冲区,由ByteBuf实现线程
从新建立一个新的ByteBuffer,并将以前的ByteBuffer复制到新建立的ByteBuffer中,最后释放老的ByteBuffer3d
重用discard 区域,须要调用discardReadBytes方法指针
Heap Buffer:堆内存字节缓存区,特色是内存的分配和回收速度快,但在I/O读写时须要额外一次内存复制,将堆内存对应的缓冲区复制到内核Channel中。
Direct Buffer:直接内存字节缓存区,直接从Socket Channel中读写数据,少了一次内存复制,但内存的分配和回收速度慢一些。
Composite Buffer:复合缓冲区,咱们能够建立多个不一样的ByteBuf,而后提供一个这些ByteBuf 组合的视图。复合缓冲区就像一个列表,咱们能够动态的添加和删除其中的ByteBuf
ByteBuf扩展了ReferenceCountered接口,这个接口定义的功能主要是引用计数。 经过refCnt的数值来计算ByteBuf,若是refCnt等于1了,调用deallocate来回收ByteBuf,对于池化的ByteBuf则放入内存池,其余则释放内存。
Netty使用Recycler来实现轻量级缓冲对象池。
Handlenetty
定义接口code
Stack对象
Stack具体维护着对象池数据,向Recycler提供push和pop两个主要访问接口,pop用于从内部弹出一个可被重复使用的对象,push用于回收之后能够重复使用的对象blog
elements接口
对象池数组
Netty中每一个线程都关联着一个内存对象池,用来操做缓冲对象
Netty中的I/O操做都须要涉及缓冲区,若是不使用内存池技术,系统将频繁的建立和回收buffer,对buffer的管理将十分困难。对于HeapBuf和Directbuf都分配和释放频率,而且对于Directbuf弥补必定的分配回收代价。
Netty 提供了 CompositeByteBuf 类, 它能够将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝
经过 wrap 操做, 咱们能够将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操做
ByteBuf 支持 slice 操做, 所以能够将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝
经过 channel的tranferTo 实现文件传输, 能够直接将文件缓冲区的数据发送到目标 Channel, 避免了传统经过循环 write 方式致使的内存拷贝问题.