Netty之NIO缓冲区

在JAVA NIO相关的组件中,ByteBuffer是除了Selector、Channel之外的另一个很重要的组件,它是直接和Channel打交道的缓冲区,通常场景或是从ByteBuffer写入Channel,或是从Channel读入Buffer;而在Netty中,被精心设计的ByteBuf则是Netty贯穿整个开发过程中的核心缓冲区,那么他们俩有什么区别呢?Netty对于缓冲区的设计对于高性能应用又带来了哪些值得借鉴的思路呢?本文在介绍ByteBuffer和ByteBuf基本概念的基础之上对两者进行对比,进而扩展介绍Netty中的ByteBuf大家族。

JAVA NIO之ByteBuffer

JAVA NIO中,Channel作为通往具有I/O操作属性的实体的抽象,这里的I/O操作通常指readding/writing,而具有I/O操作属性的实体比如I/O设备、文件、网络套接字等等。光有Channel可不行,我们必须为他增加readding/writing的特性,因此JAVA NIO基于Channel扩展WritableByteChannel和ReadableByteChannel接口。由于本文的重点是ByteBuffer,因此我们对于Channel的设计就看到这里,因为有了WritableByteChannel和ReadableByteChannel之后,我们就可以对ByteBuffer进行操作啦,看看他们提供的两个接口:

public int read(ByteBuffer dst) throws IOException;
public int write(ByteBuffer src) throws IOException;

从上面的接口我们可以看到Channel和ByteBuffer之间发生的两个基本行为,即readding/writing。无论是对文件(FileChannel)还是对网络(SocketChannel)的读写,他们都会去实现这两个基本行为。好了,我们已经从总体上认识ByteBuffer在JAVA NIO所处的位置和担当的角色了,下面我们继续深入一点认识ByteBuffer。

ByteBuffer有四个重要的属性,分别为:mark、position、limit、capacity,和两个重要方法分别为:flip和clear。ByteBuffer的底层存储结构对于堆内存和直接内存分别表现为堆上的一个byte[]对象和直接内存上分配的一块内存区域。既然是一块内存区域,那么我们就可以对其进行基于字节的读和写,而ByteBuffer的四个int类型的属性则是指向这块区域的指针:
1 position:读写指针,代表当前读或写操作的位置,这个值总是小于等于limit的。
2 mark:在使用ByteBuffer的过程中,如果想要记住当前的position,则会将当前的position值给mark,让需要恢复的时候,再将mark的值给position。
3 capacity:代表这块内存区域的大小。
4 limit:初始的Buffer中,limit和capacity的值是相等的,通常在clear操作和flip操作的时候会对这个值进行操作,在clear操作的时候会将这个值和5 capacity的值设置为相等,当flip的时候会将当前的position的值给limit,我们可以总结在写的时候,limit的值代表最大的可写位置,在读的时候,limit的值代表最大的可读位置。clear是为了写作准备、flip是为了读做准备。


ByteBuffer指针示意图

在JAVA NIO中,原生的ByteByffer家族成员很简单,主要是HeapByteBuffer、DirectByteBuffer和MappedByteBuffer:

1 HeapByteBuffer是基于堆上字节数组为存储结构的缓冲区。
2 DirectByteBuffer是基于直接内存上的内存区域为存储结构的缓冲区。
3 MappedByteBuffer主要是文件操作相关的,它提供了一种基于虚拟内存映射的机制,使得我们可以像操作文件一样来操作文件,而不需要每次将内容更新到文件之中,同时读写效率非常高。

Netty之ByteBuffer

相比于ByteBuffer的读写指针position,ByteBuf提供了两个指针readerIndex和writeIndex来分别指向读的位置和写的位置,不需要每次为读写做准备,直接设置读写指针进行读写操作即可。我们看看处于中间状态的状态:


读写中间状态的Buffer

从开始到readerIndex指针之间的这块区域是可以被丢弃的区域,后面会讲到,readerIndex和writerIndex指针之间的区域是可以被读的,writerIndex和capacity指针之间的区域是可以写的区域。当writerIndex指针到达顶端之后,ByteBuf允许用户复用之前已经被读过的区域,调用discardReadBytes方法即可,对应于上面的状态,调用discardReadBytes之后的状态如下:


调用discardReadBytes之后回收可用区域

除了discardReadBytes方法之外,另外一个比较重要的方法就是clear了,clear即清除缓冲区的指针状态,回复到初始值,对应于中间状态的那张图,调用clear之后的状态如下:
调用clear之后,Buffer状态的指针状态得到了初始化

你可能感兴趣的:(Netty之NIO缓冲区)