Buffer
BIO的局限性
Server端應該使用盡肯能少的線程來處理多個client請求。BIO下,每個client都要創建一個對應的線程來處理,線程數量越多,上限文切換造成的資源損耗越大。在BIO中使用線程池,就意味著線程池中的維護的線程數就是server端支持最多有多少個client來連接。
NIO Buffer
一個Buffer對象是固定數量的數據的容器。其作用是一個存儲器或分段運輸區,在這里數據可以被存儲并在之后用于檢索。對于每個非布爾原始數據類型都有一個緩沖區類。盡管緩沖區作用于他們存儲的原始數據類型,但緩沖區十分傾向于處理字節。緩沖區的工作與通道緊密聯系。通道是I/O傳輸發生是通過的入口,而緩沖區是這些數據傳輸的來源或目標。對于離開緩沖區的傳輸,是將緩沖區的數據傳送到通道。對于傳回緩沖區的傳輸,是將通道的數據放置在緩沖區中。這種在協同對象之間進行的緩沖區數據傳遞是高校數據處理的關鍵。
Buffer定義所有緩沖區類型共有的曹鄒,無論是它們所包含的數據類型還是可能具有的特定行為。
緩沖區是包在一個對象內的基本數據元素數組。Buffer類的優點是他將關于數據的數據內容和信息包含在一個單一的對象中。Buffer類以及它專有的子類定義了一個用于處理數據緩沖的API。
public abstract class Buffer{
// mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0';
private int limit;
private int capacity;
public final int capacity();
public final int position();
public final Buffer position(int newPosition);
public final int limit();
public final Buffer limit(int newLimit);
public final Buffer mark();
public final Buffer reset();
public final Buffer clear();
public final Buffer flip();
public final Buffer rewind();
public final int remaining();
public final boolean hasRemaining();
public abstract boolean isReadOnly();
public abstract boolean hasArray();
public abstract Object array();
public abstract int arrayOffset();
public abstract boolean isDirect();
}
容量(Capacity):緩沖區能夠容納的數據元素的最大數量,可以理解為數組的長度。在緩沖區創建時被設定,并且永不能改變
上界(Limit):緩沖區的第一個不能被讀或寫的元素的索引。或說緩沖區中現存元素的技數
位置(Position):下一個要被讀寫的元素的索引。Buffer類提供了get()和put()函數來讀取或存入數據,position位置會自動進行相應的更新
標記(Mark):一個備忘位置。調用mark()來設定mark = position。調用reset()設定position = mark。標記在設定前是未定義的。
public abstract class ByteBuffer{
public static ByteBuffer allocateDirect(int capacity);
public static ByteBuffer allocate(int capacity);
public static ByteBuffer wrap(byte[] array);
public static ByteBuffer wrap(byte[] array, int offset, int length);
public abstract byte get();
public abstract byte get(int index);
public abstract ByteBuffer put(byte b);
public abstract ByteBuffer put(int index, int b);
}
新的緩沖區是由分配(allocate)或包裝(wrap)操作創建的。allocate操作創建一個緩沖區對象并分配一個私有的空間來儲存容量大小的數據元素。wrap操作創建一個緩沖區對象但是不分配任何空間來儲存數據元素。它使用你所提供的的數組作為存儲空間來儲存穿沖去中的數據元素。
存儲操作時通過get和put操作進行的,get和put可以使相對的或是絕對的。相對方案是不帶有索引參數的函數。當相對函數被調用時,位置在返回時前進一。若位置前進過多,相對運算就會拋出異常。絕對存取不會影響緩沖區的位置屬性,但若提供的索引超出范圍,也將拋出IndexOutOfBoundsException異常。
flip()函數可以將position值重新設為0,同時將limit設置為當當前緩沖區內的最后一個元素的索引。rewind()將position設置為0單不會改變limit的值。
clear()函數可以讓緩沖區恢復到初始狀態,但并不改變緩沖區中的任何數據元素。即position=0, limit=capacity,mark=-1。
mark()方法能使緩沖區記住一個position并在之后將其返回。緩沖區的標記在mark()行數被調用之前是未定義的,值為-1,調用時mark被設為當前的position的值。reset()函數將position設為當前的mark值。若mark未定義,調用reset()將導致InvalidMarkException異常。
compact()方法會將未讀數據元素需要下移以使第一個元素索引為0。
復制緩沖區
緩沖區的復制有兩種:
完全復制:調用duplicate()或asReadOnluBuffer()函數
部分復制:調用slice()
duolicate()函數創建了一個與原始緩沖區相似的新緩沖區。兩個緩沖區共享數據元素,擁有同樣的容量,但每個緩沖區擁有各自的位置,上界和標記屬性。對一個緩沖區的數據元素所做的改變會反映在另一個緩沖區上。若原始的緩沖區為只讀或直接緩沖區,那新的緩沖區將繼承這些屬性。
CharBuffer buffer = CharBuffer.allocate(8); buffer.position(3).limit(6).mark().position(5); CharBuffer dupBuffer = buffer.duplicate(); buffer.clear();
asReadOnlyBuffer()函數來生成一個只讀的緩沖區視圖。這個新的緩沖區不允許使用put(),并且isReadOnly()會返回true。對這一只讀緩沖區調用put()函數會導致ReadOnlyBufferException異常
直接緩沖區
直接字節緩沖區通常是I/O操作最好的選擇。在設計方面,它們支持JVM可用的最高效I/O機制。非直接字節緩沖區可以被傳遞給通道,但這樣可能會導致性能損耗。通常非直接緩沖不可能成為一個本地I/O操作的目標。若向一個通道中傳遞一個非直接ByteBuffer對象用于寫入,通道可能會在每次調用中隱含地進行下面的操作:
1. 創建一個臨時的直接ByteBuffer對象
2. 將非直接緩沖區的內容復制到臨時緩沖區中
3. 使用臨時緩沖區執行低層次I/O操作
4. 臨時緩沖區對象離開作用域,并最終成為被回收的無用數據
直接緩沖區是I/O的最佳選擇,但可能比創建非直接緩沖區要花費更高的成本。直接緩沖區使用的內存是通過調用給本地操作系統方面的代碼分配的,繞過了JVM堆棧。建立和銷毀直接緩沖區會明顯比具有堆棧的緩沖區更加破費,具體取決于操作系統和JVM的實現。直接緩沖區的內存區域不受無用存儲單元收集支配。
直接ByteBuffer是通過調用具有所需容量的ByteBuffer.allocateDirect()函數產生的。通常用wrap()函數所創建的被包裝的緩沖區總是非直接的。通過緩沖區的isDirect()方法來判斷是否是直接緩沖區。
讀取數據總是需要通過內核空間傳遞到用戶空間,而往外寫數據總是要通過用戶空間到內核空間。JVM堆棧屬于用戶空間。直接緩沖區就是內核空間。內核空間的存在java中是通過Unsafe這個類來調用的。
內存映射緩沖區
映射緩沖區是帶有存儲的文件,通過內存映射來存取數據元素的字節緩沖區。映射緩沖區通常是直接存取內存的,只能通過FileChannel類來創建。映射緩沖區的用法和直接緩沖區類似,但MappedByteBuffer對象可以獨立于文件存取形式的許多特定字符。
MappedByteBuffer在大文件處理方面性能比較好。
總結
- 上一篇: 安全工具(免费杀毒软件Avast、免费防
- 下一篇: Collection