標籤:sdi 虛擬 blog 調用 目的 nio string 回收 記憶體地區
緩衝區(Buffer):一個用於特定基礎資料型別 (Elementary Data Type)的容器,由 java.nio 包定義的,所有緩衝區都是 Buffer 抽象類別的子類。
Java NIO 中的Buffer 主要用於和NIO中的通道(Channel)進行互動, 資料從通道(Channel)讀入緩衝區(Buffer)或者從緩衝區(Buffer)寫入通道(Channel)。如下,我畫的一個簡圖,Chanenl直接和資料來源或者目的位置接觸,Buffer作為中介這,從一個Channel中讀取資料,然後將資料寫入另一個Channel中。
Buffer 就像一個數組,可以儲存多個相同類型的資料。根據資料類型不同(boolean 除外) ,有以下 Buffer 常用子類:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
上述 Buffer 類 他們都採用相似的方法進行管理資料,只是各自管理的資料類型不同而已。都是通過如下方法擷取一個 Buffer對象:
static XxxBuffer allocate(int capacity) : 建立一個容量為capacity 的 XxxBuffer 對象 ,如下建立了一個容量為1024的ByteBuffer 。
ByteBuffer buffer = ByteBuffer.allocate(1024);
前面說Buffer就像一個資料,實質Buffer的內部存放資料的就是一個對應類型的數組,如下看源碼:
其他類型的Buffer類似。
Buffer的幾個主要屬性:
position : 下一個要讀取或者寫入資料的索引,其值不能為負不能大於limit。
limit : 第一個不應該讀取或者寫入資料的索引,即位於limit的資料不可讀寫,其值不能為負不能大於總容量。
capacity : 建立Buffer的最大容量,不能為負值,建立後不能修改。
如超類變數的定義:
1. 如下建立一個容量為 10 的Buffer (下標為 1-9 ,下標為10的不在Buffer容量中):
position : 0
capacity :10
limit :10
2. 向中的Buffer寫入五個元素後:
position : 5
capacity :10
limit :10
3. 使用 buffer.flip()函數轉換為讀模式後:
position : 5
capacity :10
limit :10
mark 為標記一個操作過了的位置。
所以有: mark <= position <= limit <= capacity
Buffer的幾個常用函數:
Buffer clear() :清空緩衝區並返回對緩衝區的引用
Buffer flip():將緩衝區的界限設定為當前位置,並將當前位置儲值為 0
int capacity():返回 Buffer 的 capacity 大小
boolean hasRemaining():判斷緩衝區中是否還有元素
int limit():返回 Buffer 的界限(limit) 的位置
Buffer limit(int n):將設定緩衝區界限為 n, 並返回一個具有新 limit 的緩衝區對象
Buffer mark():對緩衝區設定標記
int position():返回緩衝區的當前位置 position
Buffer position(int n):將設定緩衝區的當前位置為 n , 並返回修改後的 Buffer 對象
int remaining():返回 position 和 limit 之間的元素個數
Buffer reset() :將位置 position 轉到以前設定的mark 所在的位置
Buffer rewind():將位置設為為 0, 取消設定的 mark
下面用一段代碼來看各個方法調用後,position,capacity,limit的變化:
public class BufferTest { @Test public void test(){ String str = "abcde"; //1. 分配一個指定大小的緩衝區 ByteBuffer buf = ByteBuffer.allocate(1024); System.out.println("-----------------allocate()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //2. 利用 put() 存入資料到緩衝區中 buf.put(str.getBytes()); System.out.println("-----------------put()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //3. 切換讀取資料模式 buf.flip(); System.out.println("-----------------flip()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //4. 利用 get() 讀取緩衝區中的資料 byte[] dst = new byte[buf.limit()]; buf.get(dst); System.out.println(new String(dst, 0, dst.length)); System.out.println("-----------------get()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //5. rewind() : 可重複讀 buf.rewind(); System.out.println("-----------------rewind()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //6. clear() : 清空緩衝區. 但是緩衝區中的資料依然存在,但是處於“被遺忘”狀態 buf.clear(); System.out.println("-----------------clear()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); System.out.println((char)buf.get()); }}
運行結果:
-----------------allocate()----------------
0
1024
1024
-----------------put()----------------
5
1024
1024
-----------------flip()----------------
0
5
1024
abcde
-----------------get()----------------
5
5
1024
-----------------rewind()----------------
0
5
1024
-----------------clear()----------------
0
1024
1024
a
Buffer的存取方法:
擷取 Buffer 中的資料
get() :讀取單個位元組
get(byte[] dst):批量讀取多個位元組到 dst 中
get(int index):讀取指定索引位置的位元組(不會移動 position)
放入資料到 Buffer 中
put(byte b):將給定單個位元組寫入緩衝區的當前位置
put(byte[] src):將 src 中的位元組寫入緩衝區的當前位置
put(int index, byte b):將指定位元組寫入緩衝區的索引位置(不會移動 position)
非直接緩衝和直接緩衝區(對於位元組緩衝區而言)
1. 非直接緩衝區
ByteBuffer buf = ByteBuffer.allocate(1024);
2. 直接緩衝區
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
① 位元組緩衝區要麼是直接的,要麼是非直接的。如果為直接位元組緩衝區,則 JAVA 虛擬機器會盡最大努力直接在此緩衝區上執行本機 I/O 操作。也就是說,在每次調用基礎作業系統的一個本機 I/O 操作之前(或之後),虛擬機器都會盡量避免將緩衝區的內容複寫到中間緩衝區中(或從中間緩衝區中複製內容)。
② 直接位元組緩衝區可以通過調用此類的 allocateDirect() Factory 方法來建立。此方法返回的緩衝區進行分配和取消分配所需成本通常高於非直接緩衝區。直接緩衝區的內容可以駐留在常規的記憶體回收堆之外,因此,它們對應用程式的記憶體需求量造成的影響可能並不明顯。所以,建議將直接緩衝區主要分配給那些易受基礎系統的本機 I/O 操作影響的大型、持久的緩衝區。一般情況下,最好僅在直接緩衝區能在程式效能方面帶來明顯好處時分配它們。
③ 直接位元組緩衝區還可以通過FileChannel 的 map() 方法 將檔案地區直接映射到記憶體中來建立。該方法返回MappedByteBuffer 。Java 平台的實現有助於通過 JNI 從機器碼建立直接位元組緩衝區。如果以上這些緩衝區中的某個緩衝區執行個體指的是不可訪問的記憶體地區,則試圖訪問該地區不會更改該緩衝區的內容,並且將會在訪問期間或稍後的某個時間導致拋出不確定的異常。
④位元組緩衝區是直接緩衝區還是非直接緩衝區可通過調用其 isDirect() 方法來確定。提供此方法是為了能夠在效能關鍵型代碼中執行顯式緩衝區管理。
參考資料:李賀飛 老師的視頻教程
Java NIO (二) 緩衝區(Buffer)