NIO源码分析之Buffer

NIO源码分析之Buffer

NIO博大精深,探寻NIO的源码后收获很多,在此进行记录。这次主要分析Buffer的源码,主要有以下几个内容。

  • 绝对方法和相对方法(position,limit,capacity属性的含义)
  • Clearing,flipping,and rewinding 的作用
  • 线程不安全
  • 链式调用
  • 常用API源码分析

绝对方法和相对方法

  1. 相对方法: limit值与position值会在操作时被考虑到.
  2. 绝对方法,完全忽略掉limit值与position值.

三个重要属性的含义: position, limit, capacity

源码中的解析:
A buffer’s capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes.

一个buffer的capacity是它锁包含的元素的数量。buffer的capacity不为负而且不能改变。

A buffer’s limit is the index of the first element that should not be read or written. A buffer’s limit is never negative and is never greater than its capacity.

一个buffer的limit是第一个不能被读写的元素的索引。一个buffer的limit不能为负而且不能大于它的capacity。

A buffer’s position is the index of the next element to be read or written. A buffer’s position is never negative and is never greater than its limit.

一个buffer 的position是下一个被读写的元素的索引。一个buffer 的position不能为负而且不能大于limit。

创建时调用Buffer的一个子类ByteBuffer的 allocate申请空间。返回的是HeapByteBuffer(capacity, capacity)

1559900213755

层层跟进

1559900229280

它调用了父类ByteBuffer中的构造方法

1559900251780

由此可得出cpacity和limit的初始值是相同的,position为0。

另外调用ByteBuffer的allocateDirect,为

1559900299881

它调用了DirectByteBuffer的构造方法来进行初始化。

1559900323457

可见limit和capacity依然是相同的。position为0。

所以初始时为:

1559898867716

其中capacity永不变。读了四个以后为:

1559898879731

flip方法:将p指向0,将limit指向原来position的位置。

1559898898600

这样进行写或者读的时候,随着position向limit的移动就可以成功读取响应的数据。
mark作用为进行标记,以便返回标记处。
0 <= mark <= position <= limit <= capacity

Clearing, flipping, and rewinding

  • clear makes a buffer ready for a new sequence of channel-read or relative put operations: It sets the limit to the capacity and the position to zero.
    使得limit和capacity归零,相当于重置。
  • flip makes a buffer ready for a new sequence of channel-write or relative get operations: It sets the limit to the current position and then sets the position to zero.
    使得limit放在position的位置,使position归零,用于下一次读写。
  • rewind makes a buffer ready for re-reading the data that it already contains: It leaves the limit unchanged and sets the position to zero.
    使得position归零,进行新的读写相当于恢复为上一次读写前的状态,进行新的读写

线程不安全

Thread safety
Buffers are not safe for use by multiple concurrent threads. If a buffer is to be used by more than one thread then access to the buffer should be controlled by appropriate synchronization.

使用多线程的时候Buffer不是多线程的,如果Buffer要在多于一个线程中使用,需要进行适当的同步。

链式调用

Invocation chaining
Methods in this class that do not otherwise have a value to return are specified to return the buffer upon which they are invoked. This allows method invocations to be chained; for example, the sequence of statements

此类中没有要返回的值的方法被指定为返回调用它们的缓冲区。这允许将方法调用链接起来;例如,语句序列

b.flip();
b.position(23);
b.limit(42);
can be replaced by the single, more compact statement
b.flip().position(23).limit(42);

常用API源码分析

  • allocate()

1559900988375

HeapIntBuffer与IntBuffer为父子关系。

1559901004884

IntBuffer调用父类 IntBuffer的构造方法进行初始化

1559901021472

  • allocateDerect() 零拷贝

直接申请堆外内存.实现了零拷贝.之前为什么要拷贝?因为直接操作堆上的内存(可以看做用户空间的内存),然后分配一个address给buffer的话,期间可能会出现GC等,导致数据内存地址发生改变.所以只好把内容拷贝给buffer。直接操作堆外内存(内核空间的内存)的话,不会出现GC,所以把地址直接给buffer,可以实现零拷贝。

1559901063264

进入DerecByteBuffer()方法,如下图,以看出里面用JNI,unsafe的方法直接申请内存

1559901126260

在Buffer类中可以找到address变量,表示在对外内存分配的内存的地址,为什么不直接放在DerectByteBuffer呢,上面注释说了,为了加快GetDirectBufferAddress的调用方法.

1559901151138

  • mark()

1559901454025

将mark标记设置为position处

  • reset()

1559901481681

重新设置position位置为之前标记的m处

  • clear()

1559901520443

清空buffer,想象一下,即将position置零(最左端),limit被赋值为capacity(limit和capacity在最右端),一切都回到了最开始的地方.
注意: 这个数组不会理所当然的被抹去,这个数组会随着之后的写入而把之前的值给覆盖.

  • flip()

1559901567243

翻转,意思就是把limit置为position,position置为0,mark置为-1(代表丢弃),准备给channel进行读写.

  • rewind()

1559901608889

非常好理解,倒带,就是把position置为0,重复利用buffer,解进行下一次channel-write或者get操作.它和flip()的区别是,flip操作了limit,而这个limit是定的,只是为了再次重复利用buffer.

  • remaining()&&hasRemaining()

1559901643187

remaining意思为剩余,顾名思义,remaining()方法利用limit - position成功得出剩余的处理的数量.
hasRemaining返回一个boolean值,表示是否还有剩余.

  • slice()

1559901716482

Slice Buffer 和 原有 Buffer 共享相同的底层数组,但是不共享limit和position

  • asReadOnlyBuffer()

1559901749370

点开实现类,为 HeapByteBufferR,

1559901768633

它的put方法全都抛出ReadOnlyBufferException()异常.

以上解析了部分核心API的源码,更多的源码,原理大概相似,可以自行看源码进行理解。下面会分析Selector的相关源码,对其进行更深入的理解。

-------------本文结束感谢您的阅读-------------
愿你所有幸运,都不期而遇;愿你所有美好,都如约而至。