Buffer的重要field
buffer是线性、有限、指定类型的数据缓冲集合,在nio的定义中,buffer的关键属性:
capacity:最多能承载的元素数,永远非负且不会改变limit:首个不能被读写的元素的index,永远非负且不会大于capacity,因为下标从0开始,limit=capacity时表示整个buffer可用position:下一个应当被读写的元素的index,永远非负且不会大于limitmark:用于reset的标记,当mark未被定义时,它是-1;当它被定义时,它永远非负且不会大于position
所以,我们有了以下的约束:0<=mark<=position<=limit<=capacity
ByteBuffer为例的读写操作
先研究读写操作对上述field的操作和要求,可以更好的理解之后部分对于Buffer给出的一些操作方法的意义。
对ByteBuffer的读写操作,最终会落到其声明为abstract的单个读写操作get()和put()上。
get()
ByteBuffer对它的约定是:读取position处的一个字节,并increment作为index的position。
以HeapByteBuffer为例,它的实体是分配在堆上的字节数组,其get实现方法为:
public byte get() {return hb[ix(nextGetIndex())];}
ix添加offset偏移,nextGetIndex()则检查后increment position,同时返回旧position。
put()
ByteBuffer对它的约定与get()类似,将一个字节写入position并increment之,实现省略。
HeapByteBuffer和DirectByteBuffer的区别
从实现上可以看到,hbb的操作对象是内存中的字节数组,而dbb则利用Unsafe提供的接口直接根据address读写内存,查阅资料可以得到,这个直接内存其实也是java进程malloc的内存,但相对于Java程序是堆外内存,可由jvm参数配置,不参与minorGC,同时也可以在配置的情况下不参与fullGC,总之是块法外之地,hbb向channel写数据时,首先需要将数据拷贝到DirectBuffer,这就意味着直接申请dbb可以省略一次拷贝,所以一般设计channel读写的buffer都申请为dbb。
XX:MaxDirectMemorySize参数可以配置直接内存大小,这块内存应由程序员悉心管理。
clear、flip、rewind
这三个方法在Buffer类中就已经被定义并声明为final,这些操作定义了基于上述field控制的buffer读写操纵手法。
clear
public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;}
重置position到0,把limit设到最大,清除mark,相当与清除缓冲区的状态,invalid数据,并使得整个缓冲区可用。一般用于向buffer写入数据的准备。
filp
public final Buffer flip() {limit = position;position = 0;mark = -1;return this;}
清除mark并使position和limit夹紧已读取的数据部分,该操作用于“翻转”读写操作,在一系列的读操作之后,通过filp()的调用使得从buffer读取操作准备就绪(写完了要读)。
rewind
public final Buffer rewind() {position = 0;mark = -1;return this;}
清除mark并重置position,它用于一系列从buffer中读操作之后,其作用是重新读取上一波中已读取的数据。
mark和reset
这个机制使得操作Buffer时,能够提供比rewind更灵活的机制,用于在读写过程中提供回溯的机制。
public final Buffer mark() {mark = position;return this;}public final Buffer reset() {int m = mark;if (m < 0)throw new InvalidMarkException();position = m;// 这里引入m似乎是为了线程安全考虑?但Buffer并非是线程安全的结构return this;}
线程安全
显然,nio提供的Buffer并没有提供原子操作,因此不是线程安全的
存在的问题
Java nio提供的Buffer仍然存在一系列问题,如不能读写分离:filp只能支持由写模式转换为读模式,而在读过程中却无法插入写操作,否则就会覆盖需要读的内容,因此,netty重写了用于channel读写的buffer格式。
