本篇主要講解如何使用直接內存(堆外內存),並按照下面的步驟進行說明:html
相關背景-->讀寫操做-->關鍵屬性-->讀寫實踐-->擴展-->參考說明
但願對想使用直接內存的朋友,提供點快捷的參考。java
下面這些,都是在使用DirectBuffer
中必備的一些常識,暫做了解吧!若是想要深刻理解,能夠看看下面參考的那些博客。數組
在Java中有不少的基本類型,好比:this
byte
,一個字節是8位bit,也就是1Bshort
,16位bit,也就是2Bint
,32位bit,也就是4Blong
, 64位bit,也就是8Bchar
,16位bit,也就是2Bfloat
,32位bit,也就是4Bdouble
,64位bit,也就是8B不一樣的類型都會按照本身的位數來存儲,而且能夠自動進行轉換提高。
byte
、char
、short
均可以自動提高爲int
,若是操做數有long
,就會自動提高爲long
,float
和double
也是如此。.net
因爲一個數據類型可能有不少個字節組成的,那麼它們是如何擺放的。這個是有講究的:指針
舉個例子,一個char
是有兩個字節組成的,這兩個字節存儲可能會顯示成以下的模樣,好比字符a
:code
低地址位 高地址位 大端; 00 96 小端: 96 00
再說說"hello"
和new String("hello")
的區別:htm
若是是"hello"
,JVM會先去共享的字符串池中查找,有沒有"hello"
這個詞,若是有直接返回它的引用;若是沒有,就會建立這個對象,再返回。所以,"a"+"b"
至關於存在3個對象,分別是"a"
、"b"
、"ab"
。對象
而new String("hello")
,則省去了查找的過程,直接就建立一個hello
的對象,而且返回引用。blog
在直接內存中,經過allocateDirect(int byte_length)
申請直接內存。這段內存能夠理解爲一段普通的基於Byte
的數組,所以插入和讀取都跟普通的數組差很少。
只不過提供了基於不一樣數據類型的插入方法,好比:
等等....詳細的使用方法,也能夠參考下面的圖片:
對應讀取數據,跟寫入差很少:
注意全部沒有index參數的方法,都是按照當前position的位置進行操做的。
下面看看什麼是position,還有什麼其餘的屬性吧!
它有幾個關鍵的指標:
mark-->position-->limit-->capacity
另外,還有remaining=limit-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
是當前申請的直接內存的容量,它是申請後就不會改變的。
咱們可能想要改變這段直接內存的大小,所以能夠經過一個叫作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,就是一個標記爲而已,記錄當前的position的值。經常使用的場景,就是記錄某一次插入數據的位置,方便下一次進行回溯。
mark()
方法進行標記,reset()
方法進行清除,rewind()
方法進行初始化//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
則表示當前的剩餘空間:
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]
上面的讀寫例子中,有幾個經常使用的方法:
這個方法用於清除mark和position,還有limit的位置:
public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; }
這個方法主要用於改變當前的Position爲limit,主要是用於讀取操做。
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
這個方法在讀取一部分數據的時候比較經常使用。
它會把當前的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
這個方法用於判斷是不是直接內存。若是是返回true,若是不是返回false。
這個方法用於重置mark標記:
public final Buffer rewind() { position = 0; mark = -1; return this; }
1 Java基本數據類型
2 Java中大端與小端