ByteBuffer常用方法詳解,bytebuffer詳解
緩衝區(Buffer)
緩衝區(Buffer)就是在記憶體中預留指定大小的儲存空間用來對輸入/輸出(I/O)的資料作臨時儲存,這部分預留的記憶體空間就叫做緩衝區:
使用緩衝區有這麼兩個好處:
1、減少實際的物理讀寫次數
2、緩衝區在建立時就被分配記憶體,這塊記憶體地區一直被重用,可以減少動態分配和回收記憶體的次數
舉個簡單的例子,比如A地有1w塊磚要搬到B地
由於沒有工具(緩衝區),我們一次只能搬一本,那麼就要搬1w次(實際讀寫次數)
如果A,B兩地距離很遠的話(IO效能消耗),那麼效能消耗將會很大
但是要是此時我們有輛大卡車(緩衝區),一次可運5000本,那麼2次就夠了
相比之前,效能肯定是大大提高了。
所以,buffer在IO中很重要。在舊I/O類庫中(相對java.nio包)中的BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter在其實現中都運用了緩衝區。java.nio包公開了Buffer API,使得Java程式可以直接控制和運用緩衝區。
在Java NIO中,緩衝區的作用也是用來臨時儲存資料,可以理解為是I/O操作中資料的中轉站。緩衝區直接為通道(Channel)服務,寫入資料到通道或從通道讀取資料,這樣的操利用緩衝區資料來傳遞就可以達到對資料高效處理的目的。在NIO中主要有八種緩衝區類(其中MappedByteBuffer是專門用於記憶體映射的一種ByteBuffer):
Fields
所有緩衝區都有4個屬性:capacity、limit、position、mark,並遵循:mark <= position <= limit <= capacity,下表格是對著4個屬性的解釋:
| 屬性 |
描述 |
| Capacity |
容量,即可以容納的最大資料量;在緩衝區建立時被設定並且不能改變 |
| Limit |
表示緩衝區的當前終點,不能對緩衝區超過極限的位置進行讀寫操作。且極限是可以修改的 |
| Position |
位置,下一個要被讀或寫的元素的索引,每次讀寫緩衝區資料時都會改變改值,為下次讀寫作準備 |
| Mark |
標記,調用mark()來設定mark=position,再調用reset()可以讓position恢複到標記的位置 |
Methods
1、執行個體化
java.nio.Buffer類是一個抽象類別,不能被執行個體化。Buffer類的直接子類,如ByteBuffer等也是抽象類別,所以也不能被執行個體化。
但是ByteBuffer類提供了4個靜態Factory 方法來獲得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);}
2、另外一些常用的方法
| 方法 |
描述 |
| 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[]){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()));}
自己總結了下ByteBuffer,有不足之處還望大家指教!
包含apache mina commonbyteBuffer的協助文檔,下了好幾個裡面都沒這個類
新版本的Mina已經沒有ByteBuffer了,取而代之的是IoBuffer,該類是對java.nio.ByteBuffer的一個替代(至於為什麼要替代,因為人家覺得java.nio.ByteBuffer不給力)。Mina的API文檔太簡單了,很多時候只能看源碼中的代碼和注釋來瞭解更多。
如果要瞭解Mina的IoBuffer(現在不叫ByteBuffer了),直接看java.nio.ByteBuffer就行了。
常用的方法有:
allocate、put、get、clear、flip、limit、position、mark、reset...難得說了
看懂了java.nio.ByteBuffer你就能理解IoBuffer(也就是你說的Mina的ByteBuffer)了。
java byte[] 與ByteBuffer作為中間緩衝各有什特點?
Direct vs. non-direct buffers
A byte buffer is either direct or non-direct. Given a direct byte buffer, the Java virtual machine will make a best effort to perform native I/O operations directly upon it. That is, it will attempt to avoid copying the buffer's content to (or from) an intermediate buffer before (or after) each invocation of one of the underlying operating system's native I/O operations.
A direct byte buffer may be created by invoking the allocateDirect factory method of this class. The buffers returned by this method typically have somewhat higher allocation and deallocation costs than non-direct buffers. The contents of direct buffers may reside outside of the normal garbage-collected heap, and so their impact upon the memory footprint of an application might not be obvious. It is therefore recommended that direct buffers be allocated primarily for large, long-lived buffers that are subject to the underlying system's native I/O operations. In general it is best to allocate direct buffers only when they yield a measureable gain in program performance.
A direct byte buffer may also be created by mapping a region of a file directly into memory. An implementation of the Java platform may optionally support the creation of direct byte buffers from native code via JNI. If an instance of one of these kinds of buffers refers to an inaccessible region of memory then an attempt to access that region will not change the buffer's content and ......餘下全文>>