原文 http://blog.csdn.net/u012345283/article/details/38357851java
緩衝區(Buffer)就是在內存中預留指定大小的存儲空間用來對輸入/輸出(I/O)的數據做臨時存儲,這部分預留的內存空間就叫作緩衝區:數組
使用緩衝區有這麼兩個好處:app
一、減小實際的物理讀寫次數工具
二、緩衝區在建立時就被分配內存,這塊內存區域一直被重用,能夠減小動態分配和回收內存的次數post
舉個簡單的例子,好比A地有1w塊磚要搬到B地性能
因爲沒有工具(緩衝區),咱們一次只能搬一本,那麼就要搬1w次(實際讀寫次數)測試
若是A,B兩地距離很遠的話(IO性能消耗),那麼性能消耗將會很大大數據
可是要是此時咱們有輛大卡車(緩衝區),一次可運5000本,那麼2次就夠了ui
相比以前,性能確定是大大提升了。spa
因此,buffer在IO中很重要。在舊I/O類庫中(相對java.nio包)中的BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter在其實現中都運用了緩衝區。java.nio包公開了Buffer API,使得Java程序能夠直接控制和運用緩衝區。
在Java NIO中,緩衝區的做用也是用來臨時存儲數據,能夠理解爲是I/O操做中數據的中轉站。緩衝區直接爲通道(Channel)服務,寫入數據到通道或從通道讀取數據,這樣的操利用緩衝區數據來傳遞就能夠達到對數據高效處理的目的。在NIO中主要有八種緩衝區類(其中MappedByteBuffer是專門用於內存映射的一種ByteBuffer):
全部緩衝區都有4個屬性:capacity、limit、position、mark,並遵循:mark <= position <= limit <= capacity,下表格是對着4個屬性的解釋:
屬性 | 描述 |
---|---|
Capacity | 容量,便可以容納的最大數據量;在緩衝區建立時被設定而且不能改變 |
Limit | 表示緩衝區的當前終點,不能對緩衝區超過極限的位置進行讀寫操做。且極限是能夠修改的 |
Position | 位置,下一個要被讀或寫的元素的索引,每次讀寫緩衝區數據時都會改變改值,爲下次讀寫做準備 |
Mark | 標記,調用mark()來設置mark=position,再調用reset()可讓position恢復到標記的位置 |
一、實例化
java.nio.Buffer類是一個抽象類,不能被實例化。Buffer類的直接子類,如ByteBuffer等也是抽象類,因此也不能被實例化。
可是ByteBuffer類提供了4個靜態工廠方法來得到ByteBuffer的實例:
方法 | 描述 |
---|---|
allocate(int capacity) | 從堆空間中分配一個容量大小爲capacity的byte數組做爲緩衝區的byte數據存儲器 |
allocateDirect(int capacity) | 是不使用JVM堆棧而是經過操做系統來建立內存塊用做緩衝區,它與當前操做系統可以更好的耦合,所以能進一步提升I/O操做速度。可是分配直接緩衝區的系統開銷很大,所以只有在緩衝區較大並長期存在,或者須要常常重用時,才使用這種緩衝區 |
wrap(byte[] array) | 這個緩衝區的數據會存放在byte數組中,bytes數組或buff緩衝區任何一方中數據的改動都會影響另外一方。其實ByteBuffer底層原本就有一個bytes數組負責來保存buffer緩衝區中的數據,經過allocate方法系統會幫你構造一個byte數組 |
wrap(byte[] array, int offset, int length) |
在上一個方法的基礎上能夠指定偏移量和長度,這個offset也就是包裝後byteBuffer的position,而length呢就是limit-position的大小,從而咱們能夠獲得limit的位置爲length+position(offset) |
我寫了這幾個方法的測試方法,你們能夠運行起來更容易理解
public static void main(String args[]) throws FileNotFoundException { System.out.println("----------Test allocate--------"); System.out.println("before alocate:" + Runtime.getRuntime().freeMemory()); // 若是分配的內存太小,調用Runtime.getRuntime().freeMemory()大小不會變化? // 要超過多少內存大小JVM才能感受到? ByteBuffer buffer = ByteBuffer.allocate(102400); System.out.println("buffer = " + buffer); System.out.println("after alocate:" + Runtime.getRuntime().freeMemory()); // 這部分直接用的系統內存,因此對JVM的內存沒有影響 ByteBuffer directBuffer = ByteBuffer.allocateDirect(102400); System.out.println("directBuffer = " + directBuffer); System.out.println("after direct alocate:" + Runtime.getRuntime().freeMemory()); System.out.println("----------Test wrap--------"); byte[] bytes = new byte[32]; buffer = ByteBuffer.wrap(bytes); System.out.println(buffer); buffer = ByteBuffer.wrap(bytes, 10, 10); System.out.println(buffer); }
二、另一些方法
方法 | 描述 |
---|---|
limit(), limit(10)等 | 其中讀取和設置這4個屬性的方法的命名和jQuery中的val(),val(10)相似,一個負責get,一個負責set |
reset() | 把position設置成mark的值,至關於以前作過一個標記,如今要退回到以前標記的地方 |
clear() | position = 0;limit = capacity;mark = -1; 有點初始化的味道,可是並不影響底層byte數組的內容 |
flip() | limit = position;position = 0;mark = -1; 翻轉,也就是讓flip以後的position到limit這塊區域變成以前的0到position這塊,翻轉就是將一個處於存數據狀態的緩衝區變爲一個處於準備取數據的狀態 |
rewind() | 把position設爲0,mark設爲-1,不改變limit的值 |
remaining() | return limit - position; 返回limit和position之間相對位置差 |
hasRemaining() | return position < limit 返回是否還有未讀內容 |
compact() | 把從position到limit中的內容移到0到limit-position的區域內,position和limit的取值也分別變成limit-position、capacity。若是先將positon設置到limit,再compact,那麼至關於clear() |
get() | 相對讀,從position位置讀取一個byte,並將position+1,爲下次讀寫做準備 |
get(int index) | 絕對讀,讀取byteBuffer底層的bytes中下標爲index的byte,不改變position |
get(byte[] dst, int offset, int length) | 從position位置開始相對讀,讀length個byte,並寫入dst下標從offset到offset+length的區域 |
put(byte b) | 相對寫,向position的位置寫入一個byte,並將postion+1,爲下次讀寫做準備 |
put(int index, byte b) | 絕對寫,向 byteBuffer底層的bytes中下標爲index的位置插入byte b,不改變position |
put(ByteBuffer src) | 用相對寫,把src中可讀的部分(也就是position到limit)寫入此byteBuffer |
put(byte[] src, int offset, int length) | 從src數組中的offset到offset+length區域讀取數據並使用相對寫寫入此byteBuffer |
如下爲一些測試方法:
public static void main(String args[]) throws FileNotFoundException { System.out.println("--------Test reset----------"); buffer.clear(); buffer.position(5); buffer.mark(); buffer.position(10); System.out.println("before reset:" + buffer); buffer.reset(); System.out.println("after reset:" + buffer); System.out.println("--------Test rewind--------"); buffer.clear(); buffer.position(10); buffer.limit(15); System.out.println("before rewind:" + buffer); buffer.rewind(); System.out.println("before rewind:" + buffer); System.out.println("--------Test compact--------"); buffer.clear(); buffer.put("abcd".getBytes()); System.out.println("before compact:" + buffer); System.out.println(new String(buffer.array())); buffer.flip(); System.out.println("after flip:" + buffer); System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); System.out.println("after three gets:" + buffer); System.out.println("\t" + new String(buffer.array())); buffer.compact(); System.out.println("after compact:" + buffer); System.out.println("\t" + new String(buffer.array())); System.out.println("------Test get-------------"); buffer = ByteBuffer.allocate(32); buffer.put((byte) 'a').put((byte) 'b').put((byte) 'c').put((byte) 'd') .put((byte) 'e').put((byte) 'f'); System.out.println("before flip()" + buffer); // 轉換爲讀取模式 buffer.flip(); System.out.println("before get():" + buffer); System.out.println((char) buffer.get()); System.out.println("after get():" + buffer); // get(index)不影響position的值 System.out.println((char) buffer.get(2)); System.out.println("after get(index):" + buffer); byte[] dst = new byte[10]; buffer.get(dst, 0, 2); System.out.println("after get(dst, 0, 2):" + buffer); System.out.println("\t dst:" + new String(dst)); System.out.println("buffer now is:" + buffer); System.out.println("\t" + new String(buffer.array())); System.out.println("--------Test put-------"); ByteBuffer bb = ByteBuffer.allocate(32); System.out.println("before put(byte):" + bb); System.out.println("after put(byte):" + bb.put((byte) 'z')); System.out.println("\t" + bb.put(2, (byte) 'c')); // put(2,(byte) 'c')不改變position的位置 System.out.println("after put(2,(byte) 'c'):" + bb); System.out.println("\t" + new String(bb.array())); // 這裏的buffer是 abcdef[pos=3 lim=6 cap=32] bb.put(buffer); System.out.println("after put(buffer):" + bb); System.out.println("\t" + new String(bb.array())); }