Unsafe的使用(基于例子)

Unsafe

    • intro
      • get unsafe
      • new instance
      • private field
      • exception
      • off heap memory

intro

Unsafe是不开源的,但是它无所不能。它能申请堆外内存、cas、park和unpark、修改private变量等等的。

get unsafe

public class UnsafeUtil {
    public static Unsafe getUnsafe() throws Exception{
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        return unsafe;
    }
}

通过反射获取到unsafe。

new instance

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对象拿到了实例,没有走构造器,很是神奇。

private field

对于私有变量,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。

exception

unsafe可以自己抛出异常,单纯地抛异常:

 @Test(expected = IOException.class)
    public void throwsAnException() throws Exception {
        Unsafe unsafe = UnsafeUtil.getUnsafe();
        unsafe.throwException(new IOException());
    }

off heap memory

一般我们的对象是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);


    }

我们申请的数组长度是整数的最大值。

然后往数组里面不断地放值并取出来累加。

最后一定要手动释放内存。

你可能感兴趣的:(#,并发)