Java中內存分派的幾種辦法。本站提示廣大學習愛好者:(Java中內存分派的幾種辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是Java中內存分派的幾種辦法正文
1、數組分派的下限
Java裡數組的年夜小是受限制的,由於它應用的是int類型作為數組下標。這意味著你沒法請求跨越Integer.MAX_VALUE(2^31-1)年夜小的數組。這其實不是說你請求內存的下限就是2G。你可以請求一個年夜一點的類型的數組。好比:
final long[] ar = new long[ Integer.MAX_VALUE ];
這個會分派16G -8字節,假如你設置的-Xmx參數足夠年夜的話(平日你的堆至多得保存50%以上的空間,也就是說分派16G的內存,你得設置成-Xmx24G。這只是普通的規矩,詳細分派多年夜要看現實情形)。
不幸的是,在Java裡,因為數組元素的類型的限制,你操作起內存來會比擬費事。在操作數組方面,ByteBuffer應當是最有效的一個類了,它供給了讀寫分歧的Java類型的辦法。它的缺陷是,目的數組類型必需是byte[],也就是說你分派的內存緩存最年夜只能是2G。
2、把一切數組都看成byte數組來停止操作
假定如今2G內存對我們來講遠遠不敷,假如是16G的話還算可以。我們曾經分派了一個long[],不外我們願望把它看成byte數組來停止操作。在Java裡我們得乞助下C法式員的好副手了——sun.misc.Unsafe。這個類有兩組辦法:getN(object, offset),這個辦法是要從object偏移量為offset的地位獲得一個指定類型的值並前往它,N在這裡就是代表著誰人要前往值的類型,而putN(Object,offset,value)辦法就是要把一個值寫到Object的offset的誰人地位。
不幸的是,這些辦法只能獲得或許設置某個類型的值。假如你從數組裡拷貝數據,你還須要unsafe的另外一個辦法,copyMemory(srcObject, srcOffset, destObject,destOffet,count)。這和System.arraycopy的任務方法相似,不外它拷貝的是字節而不是數組元素。
想經由過程sun.misc.Unsafe來拜訪數組的數據,你須要兩個器械:
1.數組對象裡數據的偏移量
2.拷貝的元素在數組數據裡的偏移量
Arrays和Java其余對象一樣,都有一個對象頭,它是存儲在現實的數據後面的。這個頭的長度可以經由過程unsafe.arrayBaseOffset(T[].class)辦法來獲得到,這裡T是數組元素的類型。數組元素的年夜小可以經由過程unsafe.arrayIndexScale(T[].class) 辦法獲得到。這也就是說要拜訪類型為T的第N個元素的話,你的偏移量offset應當是arrayOffset+N*arrayScale。
我們來寫個簡略的例子吧。我們分派一個long數組,然後更新它外面的幾個字節。我們把最初一個元素更新成-1(16進制的話是0xFFFF FFFF FFFF FFFF),然再逐一消除這個元素的一切字節。
final long[] ar = new long[ 1000 ];
final int index = ar.length - 1;
ar[ index ] = -1; //FFFF FFFF FFFF FFFF
System.out.println( "Before change = " + Long.toHexString( ar[ index ] ));
for ( long i = 0; i < 8; ++i )
{
unsafe.putByte( ar, longArrayOffset + 8L * index + i, (byte) 0);
System.out.println( "After change: i = " + i + ", val = " + Long.toHexString( ar[ index ] ));
}
想運轉下面 這個例子的話,得在你的測試類裡加高低面的靜態代碼塊:
private static final Unsafe unsafe;
static
{
try
{
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe)field.get(null);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);
輸入的成果是:
Before change = ffffffffffffffff
After change: i = 0, val = ffffffffffffff00
After change: i = 1, val = ffffffffffff0000
After change: i = 2, val = ffffffffff000000
After change: i = 3, val = ffffffff00000000
After change: i = 4, val = ffffff0000000000
After change: i = 5, val = ffff000000000000
After change: i = 6, val = ff00000000000000
After change: i = 7, val = 0
3、sun.misc.Unsafe的內存分派
下面也說過了,在純Java裡我們的能分派的內存年夜小是無限的。這個限制在Java的最後版本裡就曾經定上去了,誰人時刻人們都不敢相像分派好幾個G的內存是甚麼情形。不外如今曾經是年夜數據的時期了,我們須要更多的內存。在Java裡,想獲得更多的內存有兩個辦法:
1.分派很多小塊的內存,然後邏輯上把它們看成一塊持續的年夜內存來應用。
2.應用sun.misc.Unsafe.allcateMemory(long)來停止內存分派。
第一個辦法只是從算法的角度來看比擬成心思一點,所以我們照樣來看下第二個辦法。
sun.misc.Unsafe供給了一組辦法來停止內存的分派,從新分派,和釋放。它們和C的malloc/free辦法很像:
1.long Unsafe.allocateMemory(long size)——分派一塊內存空間。這塊內存能夠會包括渣滓數據(沒有主動清零)。假如分派掉敗的話會拋一個java.lang.OutOfMemoryError的異常。它會前往一個非零的內存地址(看上面的描寫)。
2.Unsafe.reallocateMemory(long address, long size)——從新分派一塊內存,把數據從舊的內存緩沖區(address指向的處所)中拷貝到的新分派的內存塊中。假如地址等於0,這個辦法和allocateMemory的後果是一樣的。它前往的是新的內存緩沖區的地址。
3.Unsafe.freeMemory(long address)——釋放一個由後面那兩辦法生成的內存緩沖區。假如address為0甚麼也不干 。
這些辦法分派的內存應當在一個被稱為單存放器地址的形式下應用:Unsafe供給了一組只接收一個地址參數的辦法(不像雙存放器形式,它們須要一個Object還有一個偏移量offset)。經由過程這類方法分派的內存可以比你在-Xmx的Java參數裡設置裝備擺設的還要年夜。
留意:Unsafe分派出來的內存是沒法停止渣滓收受接管的。你得把它當做一種正常的資本,本身去停止治理。
上面是應用Unsafe.allocateMemory分派內存的一個例子,同時它還檢討了全部內存緩沖區是否是可讀寫的:
final int size = Integer.MAX_VALUE / 2;
final long addr = unsafe.allocateMemory( size );
try
{
System.out.println( "Unsafe address = " + addr );
for ( int i = 0; i < size; ++i )
{
unsafe.putByte( addr + i, (byte) 123);
if ( unsafe.getByte( addr + i ) != 123 )
System.out.println( "Failed at offset = " + i );
}
}
finally
{
unsafe.freeMemory( addr );
}
正如你所看見的,應用sun.misc.Unsafe你可以寫出異常通用的內存拜訪的代碼:不論是Java裡分派的何種內存,你都可以隨便讀寫隨意率性類型的數據。