NIO 之 ByteBuffer实现原理
相關(guān)文章
IO、NIO、AIO 內(nèi)部原理分析
NIO 之 Selector實現(xiàn)原理
NIO 之 Channel實現(xiàn)原理
前言
Java NIO 主要由下面3部分組成:
- Buffer
- Channel
- Selector
在傳統(tǒng)IO中,流是基于字節(jié)的方式進(jìn)行讀寫的。
在NIO中,使用通道(Channel)基于緩沖區(qū)數(shù)據(jù)塊的讀寫。
流是基于字節(jié)一個一個的讀取和寫入。
通道是基于塊的方式進(jìn)行讀取和寫入。
Buffer 類結(jié)構(gòu)圖
Buffer 的類結(jié)構(gòu)圖如下:
從圖中發(fā)現(xiàn)java中8中基本的類型,除了boolean外,其它的都有特定的Buffer子類。
Buffer類分析
Filed
每個緩沖區(qū)都有這4個屬性,無論緩沖區(qū)是何種類型都有相同的方法來設(shè)置這些值
private int mark = -1; private int position = 0; private int limit; private int capacity;1. 標(biāo)記(mark)
初始值-1,表示未標(biāo)記。
標(biāo)記一個位置,方便以后reset重新從該位置讀取數(shù)據(jù)。
2. 位置(position)
緩沖區(qū)中讀取或?qū)懭氲南乱粋€位置。這個位置從0開始,最大值等于緩沖區(qū)的大小
//獲取緩沖區(qū)的位置 public final int position() {return position; } //設(shè)置緩沖區(qū)的位置 public final Buffer position(int newPosition) {if ((newPosition > limit) || (newPosition < 0))throw new IllegalArgumentException();position = newPosition;if (mark > position) mark = -1;return this; }3. 限度(limit)
//獲取limit位置 public final int limit() {return limit; } //設(shè)置limit位置 public final Buffer limit(int newLimit) {if ((newLimit > capacity) || (newLimit < 0))throw new IllegalArgumentException();limit = newLimit;if (position > limit) position = limit;if (mark > limit) mark = -1;return this;}4. 容量(capacity)
緩沖區(qū)可以保存元素的最大數(shù)量。該值在創(chuàng)建緩存區(qū)時指定,一旦創(chuàng)建完成后就不能修改該值。
//獲取緩沖區(qū)的容量 public final int capacity() {return capacity; }filp 方法
public final Buffer flip() {limit = position;position = 0;mark = -1;return this; }rewind 方法
public final Buffer rewind() {position = 0;mark = -1;return this; }從源碼中發(fā)現(xiàn),rewind修改了position和mark,而沒有修改limit。
1. 將position設(shè)置為0
2. 取消mark標(biāo)記
clear 方法
public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;}從clear方法中,我們發(fā)現(xiàn)Buffer中的數(shù)據(jù)沒有清空,如果通過Buffer.get(i)的方式還是可以訪問到數(shù)據(jù)的。如果再次向緩沖區(qū)中寫入數(shù)據(jù),他會覆蓋之前存在的數(shù)據(jù)。
remaining 方法
查看當(dāng)前位置和limit之間的元素數(shù)。
public final int remaining() {return limit - position; }hasRemaining 方法
判斷當(dāng)前位置和limit之間是否還有元素
public final boolean hasRemaining() {return position < limit; }ByteBuffer 類分析
從圖中我們可以發(fā)現(xiàn) ByteBuffer繼承于Buffer類,ByteBuffer是個抽象類,它有兩個實現(xiàn)的子類HeapByteBuffer和MappedByteBuffer類
HeapByteBuffer:在堆中創(chuàng)建的緩沖區(qū)。就是在jvm中創(chuàng)建的緩沖區(qū)。
MappedByteBuffer:直接緩沖區(qū)。物理內(nèi)存中創(chuàng)建緩沖區(qū),而不在堆中創(chuàng)建。
allocate 方法(創(chuàng)建堆緩沖區(qū))
public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity); }我們發(fā)現(xiàn)allocate方法創(chuàng)建的緩沖區(qū)是創(chuàng)建的HeapByteBuffer實例。
HeapByteBuffer 構(gòu)造
HeapByteBuffer(int cap, int lim) { // package-privatesuper(-1, 0, lim, cap, new byte[cap], 0); }從堆緩沖區(qū)中看出,所謂堆緩沖區(qū)就是在堆內(nèi)存中創(chuàng)建一個byte[]數(shù)組。
allocateDirect創(chuàng)建直接緩沖區(qū)
public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity); }我們發(fā)現(xiàn)allocate方法創(chuàng)建的緩沖區(qū)是創(chuàng)建的DirectByteBuffer實例。
DirectByteBuffer構(gòu)造
直接緩沖區(qū)是通過java中Unsafe類進(jìn)行在物理內(nèi)存中創(chuàng)建緩沖區(qū)。
wrap 方法
public static ByteBuffer wrap(byte[] array) public static ByteBuffer wrap(byte[] array, int offset, int length);可以通過wrap類把字節(jié)數(shù)組包裝成緩沖區(qū)ByteBuffer實例。
這里需要注意的的,把a(bǔ)rray的引用賦值給ByteBuffer對象中字節(jié)數(shù)組。如果array數(shù)組中的值更改,則ByteBuffer中的數(shù)據(jù)也會更改的。
get 方法
獲取position坐標(biāo)元素,并將position+1;
獲取指定索引下標(biāo)的元素
從當(dāng)前position中讀取元素填充到dst數(shù)組中,每填充一個元素position+1;
從當(dāng)前position中讀取元素到dst數(shù)組的offset下標(biāo)開始填充length個元素。
put 方法
寫入一個元素并position+1
指定的索引寫入一個元素
寫入一個自己數(shù)組,并position+數(shù)組長度
從一個自己數(shù)組的offset開始length個元素寫入到ByteBuffer中,并把position+length
寫入一個ByteBuffer,并position加入寫入的元素個數(shù)
視圖緩沖區(qū)
ByteBuffer可以轉(zhuǎn)換成其它類型的Buffer。例如CharBuffer、IntBuffer 等。
壓縮緩沖區(qū)
public ByteBuffer compact() {System.arraycopy(hb, ix(position()), hb, ix(0), remaining());position(remaining());limit(capacity());discardMark();return this;}1、把緩沖區(qū)positoin到limit中的元素向前移動positoin位
2、設(shè)置position為remaining()
3、 limit為緩沖區(qū)容量
4、取消標(biāo)記
例如:ByteBuffer.allowcate(10);
內(nèi)容:[0 ,1 ,2 ,3 4, 5, 6, 7, 8, 9]
compact前
[0 ,1 ,2 , 3, 4, 5, 6, 7, 8, 9]
pos=4
lim=10
cap=10
compact后
[4, 5, 6, 7, 8, 9, 6, 7, 8, 9]
pos=6
lim=10
cap=10
slice方法
public ByteBuffer slice() {return new HeapByteBuffer(hb,-1,0,this.remaining(),this.remaining(),this.position() + offset); }創(chuàng)建一個分片緩沖區(qū)。分配緩沖區(qū)與主緩沖區(qū)共享數(shù)據(jù)。
分配的起始位置是主緩沖區(qū)的position位置
容量為limit-position。
分片緩沖區(qū)無法看到主緩沖區(qū)positoin之前的元素。
直接緩沖區(qū)和堆緩沖區(qū)性能對比
下面我們從緩沖區(qū)創(chuàng)建的性能和讀取性能兩個方面進(jìn)行性能對比。
讀寫性能對比
public static void directReadWrite() throws Exception {int time = 10000000;long start = System.currentTimeMillis();ByteBuffer buffer = ByteBuffer.allocate(4*time);for(int i=0;i<time;i++){buffer.putInt(i);}buffer.flip();for(int i=0;i<time;i++){buffer.getInt();}System.out.println("堆緩沖區(qū)讀寫耗時 :"+(System.currentTimeMillis()-start));start = System.currentTimeMillis();ByteBuffer buffer2 = ByteBuffer.allocateDirect(4*time);for(int i=0;i<time;i++){buffer2.putInt(i);}buffer2.flip();for(int i=0;i<time;i++){buffer2.getInt();}System.out.println("直接緩沖區(qū)讀寫耗時:"+(System.currentTimeMillis()-start)); }輸出結(jié)果:
堆緩沖區(qū)創(chuàng)建耗時 :70 直接緩沖區(qū)創(chuàng)建耗時:47從結(jié)果中我們發(fā)現(xiàn)堆緩沖區(qū)讀寫比直接緩沖區(qū)讀寫耗時更長。
#
public static void directAllocate() throws Exception {int time = 10000000;long start = System.currentTimeMillis();for (int i = 0; i < time; i++) {ByteBuffer buffer = ByteBuffer.allocate(4);}System.out.println("堆緩沖區(qū)創(chuàng)建時間:"+(System.currentTimeMillis()-start));start = System.currentTimeMillis();for (int i = 0; i < time; i++) {ByteBuffer buffer = ByteBuffer.allocateDirect(4);}System.out.println("直接緩沖區(qū)創(chuàng)建時間:"+(System.currentTimeMillis()-start)); }輸出結(jié)果:
堆緩沖區(qū)創(chuàng)建時間:73 直接緩沖區(qū)創(chuàng)建時間:5146從結(jié)果中發(fā)現(xiàn)直接緩沖區(qū)創(chuàng)建分配空間比較耗時。
對比結(jié)論
直接緩沖區(qū)比較適合讀寫操作,最好能重復(fù)使用直接緩沖區(qū)并多次讀寫的操作。
堆緩沖區(qū)比較適合創(chuàng)建新的緩沖區(qū),并且重復(fù)讀寫不會太多的應(yīng)用。
建議:如果經(jīng)過性能測試,發(fā)現(xiàn)直接緩沖區(qū)確實比堆緩沖區(qū)效率高才使用直接緩沖區(qū),否則不建議使用直接緩沖區(qū)。
本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8????
點(diǎn)擊這里快速進(jìn)入簡書
GIT地址:http://git.oschina.net/brucekankan/
點(diǎn)擊這里快速進(jìn)入GIT
總結(jié)
以上是生活随笔為你收集整理的NIO 之 ByteBuffer实现原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高亮标红
- 下一篇: NIO 之 Channel实现原理