1、ByteBuflinux
下图是ByteBuf的继承体系:后端
1、分类api
1)从内存分配角度看,可分为两种:网络
(1)堆内存字节缓冲区:如上图中带有Heap的类,它们的特色是直接在堆中分配内存,分配和回收快,但缺点是在网络通讯读写中,需额外作一次内存分配,函数
(2)直接内存缓冲区:使用直接内存进行内存分配,如上图中带有Direct的类,它们的特色是分配回收较慢,但网络通讯中不须要进行额外的内存分配,相似于linux中的sendfile系统调用,它直接将内核缓冲区的数据复制到Channel中,再也不通过用户缓冲区,由于少了一次复制,性能有所提高。性能
在实际使用中的最佳实践是在I/O通讯线程的读写缓冲区使用直接内存缓冲区,后端业务消息的编解码使用堆内存缓冲区。线程
2)从内存回收角度看,可分为两类:指针
(1)基于对象池的ByteBuf:如继承体系中带有Pool的类,它们的内存管理基于对象池,它们本身维护了一个内存池,可提高使用效率。对象
(2)普通ByteBuf继承
基于对象池的ByteBuf使用时须要更加谨慎。
2、ByteBuf简介
1)ByteBuf与ByteBuffer的比较
ByteBuf是对NIO中ByteBuffer的封装,为何不使用原始的ByteBuffer呢?究其缘由,ByteBuffer有如下不足:
(1):长度固定,一旦分配完成,则容量不能动态扩展或收缩;
(2):读写后不方便,由于每次都须要调用flip()等函数处理;
(3):API功能有限,一些高级和实用特性不支持。
2)ByteBuf使用
建立ByteBuf的官方推荐方法是使用Unpool来建立,如:
ByteBuf buf = Unpooled.buffer(20);
相似于ByteBuffer,ByteBuf中也有读写索引等概念,不一样在于ByteBuffer使用一个位置指针处理读写操做,而ByteBuffer使用两个位置指针分别对读写进行操做,下图是ByteBuf的读写索引构造:
其中readerIndex标识读取索引,writerIndex标识写索引,capacity为容量,即readerIndex到writerIndex部分是可读部分,writerIndex到capacity部分是可写部分。
discardable bytes部分是可重用部分,这时咱们可调用相应的api重用该部份内存来增长可写内存,如调用discardReadBytes()方法则会将writerIndex置为writerIndex – readerIndex,readerIndex置为0,同时发生数据的复制,即将原来可读的内容向前移动,因此频繁调用该函数将致使性能的降低。下图是调用discardReadBytes先后的对比图:
一种更好的方法是调用clear()函数,这样不会致使内存的移动,它仅仅是移动了位置指针,不过会致使可读内容的晴空,下图是调用clear函数先后的状况:
ByteBuf中也一样支持ByteBuffer中的mark和reset操做,不过更加丰富,它支持如下四个函数:
(1)markReaderIndex
(2)markWriterIndex
(3)resetReaderIndex
(4)resetWriterIndex
ByteBuf中有Derived buffer的概念,ByteBuf提供了接口用于实现ByteBuf的复制,如如下方法:
(1)duplicate:返回当前ByteBuf的复制对象,但返回的ByteBuf对象与原始的ByteBuf共享缓冲区内容,即更改返回后ByteBuf的内容一样也会更改原始的ByteBuf中的内容,返回后的ByteBuf会维护本身独立的读写索引;
(2)copy:复制一个新的ByteBuf,与duplicate不一样的是,返回后的ByteBuf在内容上是独立的。还有copy(int index, int length),从字面上也很好理解;
(3)slice:返回当前ByteBuf的可读子缓冲区,起始位置从readerIndex到writerIndex,返回后的ByteBuf与原ByteBuf共享内容,但读写索引地理维护。
ByteBuf还可转换成标准的ByteBuffer,主要使用两个方法:
nioBuffer():将当前ByteBuf的可读缓冲区转换成ByteBuffer,但二者共享同一缓冲区内容。
nioBuffer(int index, int length):将当前ByteBuf从index开始长度为length的缓冲区转换成ByteBuffer。
ByteBuf支持一系列的随机读写操做,它们是一系列set和get操做,具体见API。