本文主要介绍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。
假设现在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的 工作方式类似,不过它拷贝的 是字节而不是数组元素。
Java培训机构温馨提示想通过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
上面也说过了,在纯Java里我们的 能分配的 内存大小是有限的 。这个限制在Java的 最初版本里就已经定下来了,那个时候人们都不敢相像分配好几个G的 内存是什么情况。不过现在已经是大数据的 时代了,我们需要更多的 内存。在Java里,想获取更多的 内存有两个方法
1.分配许多小块的 内存,然后逻辑上把它们当作一块连续的 大内存来使用。
2.使用sun.misc,广州电脑培训Java是一个广泛使用的网络编程语言,它是一种新的计算概念。首先,作为一种程序设计语言,它简单、面向对象、不依赖于机器的结构、具有可移植性、鲁棒性、安全性、并且提供了并发的机制、具有很高的性能。其次,它最大限度地利用了网络,Java的小应用程序(applet)可在网络上传输而不受CPU和环境的限制。另外,Java还提供了丰富的类库,使程序设计者可以很方便地建立自己的系统。.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 );
}
经过Java培训机构的 小编的 一番解说,你明白了吗?正如你所看见的 ,使用sun.misc.Unsafe你可以写出非常通用的 内存访问的 代码不管是Java里分配的 何种内存,你都可以随意读写任意类型的 数据。