本篇主要講解如何使用直接內存(堆外內存),并按照下面的步驟進行說明:
相關背景–>讀寫操作–>關鍵屬性–>讀寫實踐–>擴展–>參考說明
希望對想使用直接內存的朋友,提供點快捷的參考。
數據類型
下面這些,都是在使用DirectBuffer中必備的一些常識,暫作了解吧!如果想要深入理解,可以看看下面參考的那些博客。
基本類型長度
在Java中有很多的基本類型,比如:
不同的類型都會按照自己的位數來存儲,并且可以自動進行轉換提升。
byte、char、short都可以自動提升為int,如果操作數有long,就會自動提升為long,float和double也是如此。
大端小端
由于一個數據類型可能有很多個字節組成的,那么它們是如何擺放的。這個是有講究的:
舉個例子,一個char是有兩個字節組成的,這兩個字節存儲可能會顯示成如下的模樣,比如字符a:
String與new String的區別
再說說”hello”和new String(“hello”)的區別:
如果是”hello”,JVM會先去共享的字符串池中查找,有沒有”hello”這個詞,如果有直接返回它的引用;如果沒有,就會創建這個對象,再返回。因此,”a”+”b”相當于存在3個對象,分別是”a”、”b”、”ab”。
而new String(“hello”),則省去了查找的過程,直接就創建一個hello的對象,并且返回引用。
讀寫數據
在直接內存中,通過allocateDirect(int byte_length)申請直接內存。這段內存可以理解為一段普通的基于Byte的數組,因此插入和讀取都跟普通的數組差不多。
只不過提供了基于不同數據類型的插入方法,比如:
等等….詳細的使用方法,也可以參考下面的圖片:
對應讀取數據,跟寫入差不多:

注意所有沒有index參數的方法,都是按照當前position的位置進行操作的。
下面看看什么是position,還有什么其他的屬性吧!
基本的屬性值
它有幾個關鍵的指標:
mark–>position–>limit–>capacity
另外,還有remaining=limit-position。
先說說他們的意思吧!
當前位置——position
position是當前數組的指針,指示當前數據位置。舉個例子:
ByteBuffer buffer = ByteBuffer.allocateDirect(1024); buffer.putChar('a'); System.out.println(buffer); buffer.putChar('c'); System.out.println(buffer); buffer.putInt(10); System.out.println(buffer);
由于一個char是2個字節,一個Int是4個字節,因此position的位置分別是:
2,4,8
注意,Position的位置是插入數據的當前位置,如果插入數據,就會自動后移。
也就是說,如果存儲的是兩個字節的數據,position的位置是在第三個字節上,下標就是2。
java.nio.DirectByteBuffer[pos=2 lim=1024 cap=1024]
java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024]
java.nio.DirectByteBuffer[pos=8 lim=1024 cap=1024]
//position(int)方法的源碼 public final Buffer position(int newPosition) { if ((newPosition > limit) || (newPosition < 0)) throw new IllegalArgumentException();
position = newPosition; if (mark > position) mark = -1; return this;
}
注意:position的位置要比limit小,比mark大
空間容量——capacity
capacity是當前申請的直接內存的容量,它是申請后就不會改變的。
我們可能想要改變這段直接內存的大小,因此可以通過一個叫做Limit的屬性設置。
注意limit要比mark和position大,比capacity小。
//limit(int)方法的源碼 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;
}
標記位置——mark
mark,就是一個標記為而已,記錄當前的position的值。常用的場景,就是記錄某一次插入數據的位置,方便下一次進行回溯。
//mark方法標記當前的position,默認為-1 public final Buffer mark() {
mark = position; return this;
} //reset方法重置mark的位置,position的位置,不能小于mark的位置,否則會出錯 public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException();
position = m; return this;
} //重置mark為-1.position為0 public final Buffer rewind() {
position = 0;
mark = -1; return this;
}
使用案例
ByteBuffer buffer = ByteBuffer.allocateDirect(1024); buffer.putChar('a'); buffer.putChar('c'); System.out.println("插入完數據 " + buffer); buffer.mark();// 記錄mark的位置 buffer.position(30);// 設置的position一定要比mark大,否則mark無法重置 System.out.println("reset前 " + buffer); buffer.reset();// 重置reset ,reset后的position=mark System.out.println("reset后 " + buffer); buffer.rewind();//清除標記,position變成0,mark變成-1 System.out.println("清除標記后 " + buffer);
可以看到如下的運行結果:
插入完數據 java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024] reset前 java.nio.DirectByteBuffer[pos=30 lim=1024 cap=1024] reset后 java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024] 清除標記后 java.nio.DirectByteBuffer[pos=0 lim=1024 cap=1024]
剩余空間——remaing
remaing則表示當前的剩余空間:
public final int remaining() { return limit - position;
}
讀寫實踐
寫操作主要就是按照自己的數據類型,寫入到直接內存中,注意每次寫入數據的時候,position都會自動加上寫入數據的長度,指向下一個該寫入的起始位置:
下面看看如何寫入一段byte[]或者字符串:
ByteBuffer buffer = ByteBuffer.allocateDirect(10); byte[] data = {1,2}; buffer.put(data); System.out.println("寫byte[]后 " + buffer); buffer.clear(); buffer.put("hello".getBytes()); System.out.println("寫string后 " + buffer);
輸出的內容為:
寫byte[]后 java.nio.DirectByteBuffer[pos=2 lim=10 cap=10] 寫string后 java.nio.DirectByteBuffer[pos=5 lim=10 cap=10]
讀的時候,可以通過一個外部的byte[]數組進行讀取。由于沒有找到直接操作直接內存的方法: 因此如果想在JVM應用中使用直接內存,需要申請一段堆中的空間,存放數據。
如果有更好的方法,還請留言。
ByteBuffer buffer = ByteBuffer.allocateDirect(10);
buffer.put(new byte[]{1,2,3,4});
System.out.println("剛寫完數據 " +buffer);
buffer.flip();
System.out.println("flip之后 " +buffer); byte[] target = new byte[buffer.limit()];
buffer.get(target);//自動讀取target.length個數據 for(byte b : target){
System.out.println(b);
}
System.out.println("讀取完數組 " +buffer);
輸出為
剛寫完數據 java.nio.DirectByteBuffer[pos=4 lim=10 cap=10] flip之后 java.nio.DirectByteBuffer[pos=0 lim=4 cap=10] 1 2 3 4 讀取完數組 java.nio.DirectByteBuffer[pos=4 lim=4 cap=10]
常用方法
上面的讀寫例子中,有幾個常用的方法:
clear()
這個方法用于清除mark和position,還有limit的位置:
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1; return this;
}
flip()
這個方法主要用于改變當前的Position為limit,主要是用于讀取操作。
public final Buffer flip() {
limit = position;
position = 0;
mark = -1; return this;
}
compact()
這個方法在讀取一部分數據的時候比較常用。
它會把當前的Position移到0,然后position+1移到1。
public ByteBuffer compact() { int pos = position(); int lim = limit();
assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); unsafe.copyMemory(ix(pos), ix(0), rem << 0);
position(rem);
limit(capacity());
discardMark(); return this;
}
比如一段空間內容為:
123456789
當position的位置在2時,調用compact方法,會變成:
345678989
isDirect()
這個方法用于判斷是否是直接內存。如果是返回true,如果不是返回false。
rewind()
這個方法用于重置mark標記:
public final Buffer rewind() {
position = 0;
mark = -1; return this;
}
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。