Unsafe是不开源的,但是它无所不能。它能申请堆外内存、cas、park和unpark、修改private变量等等的。
通过反射获取到unsafe。
unsafe可以根据类对象得到实例。
举例:
public class InitializationOrdering { private long a; public InitializationOrdering() { this.a = 1; } public long getA() { return this.a; } } @Test public void testInitializeInstanceByNew() { InitializationOrdering initializationOrdering = new InitializationOrdering(); assertEquals(initializationOrdering.getA(), 1); }如果是new出来的,必然走构造器,那么getA()一定是1。
@Test public void testInitializeInstanceByUnsafe() throws Exception { Unsafe unsafe = UnsafeUtil.getUnsafe(); InitializationOrdering instance = (InitializationOrdering) unsafe.allocateInstance(InitializationOrdering.class); assertEquals(instance.getA(), 0); }unsafe根据class对象拿到了实例,没有走构造器,很是神奇。
对于私有变量,unsafe也能拿到。
例子:
public class SecretHolder { private int SECRET_VALUE = 0; public boolean secretIsDisclosed() { return SECRET_VALUE == 1; } } @Test public void testModifyPrivateValue() throws Exception { SecretHolder secretHolder = new SecretHolder(); Field field = secretHolder.getClass().getDeclaredField("SECRET_VALUE"); Unsafe unsafe = UnsafeUtil.getUnsafe(); unsafe.putInt(secretHolder, unsafe.objectFieldOffset(field), 1); assertTrue(secretHolder.secretIsDisclosed()); }unsafe成功地给私有变量赋上了值1。
unsafe可以自己抛出异常,单纯地抛异常:
@Test(expected = IOException.class) public void throwsAnException() throws Exception { Unsafe unsafe = UnsafeUtil.getUnsafe(); unsafe.throwException(new IOException()); }一般我们的对象是new出来的,不管你用什么设计模式,最终还是new出来的。new出来的对象一定在堆上,是处于jvm的管辖范围的,换句话说,它能被gc。
但是有些情况,我们要在堆外直接申请内存放置对象。它脱离了gc,并且空间可以要得很大。
但是我们要手动释放内存。
public class OffHeapArray { private final static int BYTE = 1; private long size; private long address; public OffHeapArray(long size) throws Exception { this.size = size; address = getUnsafe().allocateMemory(size * BYTE); } public void set(long i, byte value) throws Exception { getUnsafe().putByte(address + i * BYTE, value); } public int get(long idx) throws Exception { return getUnsafe().getByte(address + idx * BYTE); } public long size() { return size; } public void freeMemory() throws Exception { getUnsafe().freeMemory(address); } }构造方法:
public OffHeapArray(long size) throws Exception { this.size = size; address = getUnsafe().allocateMemory(size * BYTE); }分配内存,初始化地址。
set方法:
public void set(long i, byte value) throws Exception { getUnsafe().putByte(address + i * BYTE, value); }这是往索引中放值。这个索引就是地址加上偏移量。
get方法:
public int get(long idx) throws Exception { return getUnsafe().getByte(address + idx * BYTE); }根据索引取值。
测试:
@Test @Ignore public void testOffHeapMemory() throws Exception { //given long SUPER_SIZE = (long) Integer.MAX_VALUE; OffHeapArray array = new OffHeapArray(SUPER_SIZE); //when int sum = 0; for (int i = 0; i < 100; i++) { array.set((long) Integer.MAX_VALUE + i, (byte) 3); sum += array.get((long) Integer.MAX_VALUE + i); } long arraySize = array.size(); array.freeMemory(); //then assertEquals(arraySize, SUPER_SIZE); assertEquals(sum, 300); }我们申请的数组长度是整数的最大值。
然后往数组里面不断地放值并取出来累加。
最后一定要手动释放内存。