日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NIO 之 ByteBuffer实现原理

發布時間:2024/9/30 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NIO 之 ByteBuffer实现原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

相關文章

IO、NIO、AIO 內部原理分析
NIO 之 Selector實現原理
NIO 之 Channel實現原理

前言

Java NIO 主要由下面3部分組成:

  • Buffer
  • Channel
  • Selector

在傳統IO中,流是基于字節的方式進行讀寫的。
在NIO中,使用通道(Channel)基于緩沖區數據塊的讀寫。

流是基于字節一個一個的讀取和寫入。
通道是基于塊的方式進行讀取和寫入。

Buffer 類結構圖

Buffer 的類結構圖如下:

從圖中發現java中8中基本的類型,除了boolean外,其它的都有特定的Buffer子類。

Buffer類分析

Filed

每個緩沖區都有這4個屬性,無論緩沖區是何種類型都有相同的方法來設置這些值

private int mark = -1; private int position = 0; private int limit; private int capacity;

1. 標記(mark)

初始值-1,表示未標記。
標記一個位置,方便以后reset重新從該位置讀取數據。

public final Buffer mark() {mark = position;return this; }public final Buffer reset() {int m = mark;if (m < 0)throw new InvalidMarkException();position = m;return this; }

2. 位置(position)

緩沖區中讀取或寫入的下一個位置。這個位置從0開始,最大值等于緩沖區的大小

//獲取緩沖區的位置 public final int position() {return position; } //設置緩沖區的位置 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; } //設置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)

緩沖區可以保存元素的最大數量。該值在創建緩存區時指定,一旦創建完成后就不能修改該值。

//獲取緩沖區的容量 public final int capacity() {return capacity; }

filp 方法

public final Buffer flip() {limit = position;position = 0;mark = -1;return this; }
  • 將limit設置成當前position的坐標
  • 將position設置為0
  • 取消標記
  • rewind 方法

    public final Buffer rewind() {position = 0;mark = -1;return this; }

    從源碼中發現,rewind修改了position和mark,而沒有修改limit。
    1. 將position設置為0
    2. 取消mark標記

    clear 方法

    public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;}
  • 將position坐標設置為0
  • limit設置為capacity
  • 取消標記
  • 從clear方法中,我們發現Buffer中的數據沒有清空,如果通過Buffer.get(i)的方式還是可以訪問到數據的。如果再次向緩沖區中寫入數據,他會覆蓋之前存在的數據。

    remaining 方法

    查看當前位置和limit之間的元素數。

    public final int remaining() {return limit - position; }

    hasRemaining 方法

    判斷當前位置和limit之間是否還有元素

    public final boolean hasRemaining() {return position < limit; }

    ByteBuffer 類分析

    從圖中我們可以發現 ByteBuffer繼承于Buffer類,ByteBuffer是個抽象類,它有兩個實現的子類HeapByteBuffer和MappedByteBuffer類

    HeapByteBuffer:在堆中創建的緩沖區。就是在jvm中創建的緩沖區。
    MappedByteBuffer:直接緩沖區。物理內存中創建緩沖區,而不在堆中創建。

    allocate 方法(創建堆緩沖區)

    public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity); }

    我們發現allocate方法創建的緩沖區是創建的HeapByteBuffer實例。

    HeapByteBuffer 構造

    HeapByteBuffer(int cap, int lim) { // package-privatesuper(-1, 0, lim, cap, new byte[cap], 0); }

    從堆緩沖區中看出,所謂堆緩沖區就是在堆內存中創建一個byte[]數組。

    allocateDirect創建直接緩沖區

    public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity); }

    我們發現allocate方法創建的緩沖區是創建的DirectByteBuffer實例。

    DirectByteBuffer構造


    直接緩沖區是通過java中Unsafe類進行在物理內存中創建緩沖區。

    wrap 方法

    public static ByteBuffer wrap(byte[] array) public static ByteBuffer wrap(byte[] array, int offset, int length);

    可以通過wrap類把字節數組包裝成緩沖區ByteBuffer實例。
    這里需要注意的的,把array的引用賦值給ByteBuffer對象中字節數組。如果array數組中的值更改,則ByteBuffer中的數據也會更改的。

    get 方法

  • public byte get()
    獲取position坐標元素,并將position+1;
  • public byte get(int i)
    獲取指定索引下標的元素
  • public ByteBuffer get(byte[] dst)
    從當前position中讀取元素填充到dst數組中,每填充一個元素position+1;
  • public ByteBuffer get(byte[] dst, int offset, int length)
    從當前position中讀取元素到dst數組的offset下標開始填充length個元素。
  • put 方法

  • public ByteBuffer put(byte x)
    寫入一個元素并position+1
  • public ByteBuffer put(int i, byte x)
    指定的索引寫入一個元素
  • public final ByteBuffer put(byte[] src)
    寫入一個自己數組,并position+數組長度
  • public ByteBuffer put(byte[] src, int offset, int length)
    從一個自己數組的offset開始length個元素寫入到ByteBuffer中,并把position+length
  • public ByteBuffer put(ByteBuffer src)
    寫入一個ByteBuffer,并position加入寫入的元素個數
  • 視圖緩沖區


    ByteBuffer可以轉換成其它類型的Buffer。例如CharBuffer、IntBuffer 等。

    壓縮緩沖區

    public ByteBuffer compact() {System.arraycopy(hb, ix(position()), hb, ix(0), remaining());position(remaining());limit(capacity());discardMark();return this;}

    1、把緩沖區positoin到limit中的元素向前移動positoin位
    2、設置position為remaining()
    3、 limit為緩沖區容量
    4、取消標記

    例如:ByteBuffer.allowcate(10);
    內容:[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); }

    創建一個分片緩沖區。分配緩沖區與主緩沖區共享數據。
    分配的起始位置是主緩沖區的position位置
    容量為limit-position。
    分片緩沖區無法看到主緩沖區positoin之前的元素。

    直接緩沖區和堆緩沖區性能對比

    下面我們從緩沖區創建的性能和讀取性能兩個方面進行性能對比。

    讀寫性能對比

    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("堆緩沖區讀寫耗時 :"+(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("直接緩沖區讀寫耗時:"+(System.currentTimeMillis()-start)); }

    輸出結果:

    堆緩沖區創建耗時 :70 直接緩沖區創建耗時:47

    從結果中我們發現堆緩沖區讀寫比直接緩沖區讀寫耗時更長。

    #

    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("堆緩沖區創建時間:"+(System.currentTimeMillis()-start));start = System.currentTimeMillis();for (int i = 0; i < time; i++) {ByteBuffer buffer = ByteBuffer.allocateDirect(4);}System.out.println("直接緩沖區創建時間:"+(System.currentTimeMillis()-start)); }

    輸出結果:

    堆緩沖區創建時間:73 直接緩沖區創建時間:5146

    從結果中發現直接緩沖區創建分配空間比較耗時。

    對比結論

    直接緩沖區比較適合讀寫操作,最好能重復使用直接緩沖區并多次讀寫的操作。
    堆緩沖區比較適合創建新的緩沖區,并且重復讀寫不會太多的應用。

    建議:如果經過性能測試,發現直接緩沖區確實比堆緩沖區效率高才使用直接緩沖區,否則不建議使用直接緩沖區。

    本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8????
    點擊這里快速進入簡書

    GIT地址:http://git.oschina.net/brucekankan/
    點擊這里快速進入GIT

    總結

    以上是生活随笔為你收集整理的NIO 之 ByteBuffer实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。