Java中内存分配的几种方法

本文主要介绍Java中几种分配内存的方法。我们会看到如何使用sun.misc.Unsafe来统一操作任意类型的内存。以前用C语言开发的同学通常都希望能在Java中通过较底层的接口来操作内存,他们一定会对本文中要讲的内容感兴趣

一、数组分配的上限

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。

二、把所有数组都当作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 ] ));
}

以上就是Java中内存分配的几种方法的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java