Unsafe总结

前几天下班回家的路上一个客户端同事问我,java能自己控制内存吗?再加上这几天在看disruptor源码,也遇到了Unsafe相关的东西,抽个时间总结一下。

Unsafe类,下面来介绍一下这个类

Unsafe类是final类,并且构造方法是私有的

public final class Unsafe {
    private static native void registerNatives();
    static {
        registerNatives();
    }
    
    private Unsafe(){}

    private static final Unsafe theUnsafe = new Unsafe();

在Unsafe里提供了getUnsafe方法来获取Unsafe实例

public static Unsafe getUnsafe() {
    return theUnsafe;
}

但是,我们发现Unsafe在jdk.internal.misc里,外部不可访问的。不过jdk在sun.misc里还有一个Unsafe类对其进行了封装提供对外访问,我们发现构造方法还是私有的

private Unsafe() {}

private static final Unsafe theUnsafe = new Unsafe();
private static final jdk.internal.misc.Unsafe theInternalUnsafe = jdk.internal.misc.Unsafe.getUnsafe();

也提供了getUnsafe方法

public static Unsafe getUnsafe() {
    Class<?> caller = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(caller.getClassLoader))
        throw new SecurityException("Unsafe");
    retutn theUnsafe; 
}

我们发现调用它时,会检查classloader是不是系统域类加载器,发现我们还是不能直接调用它获得Unsafe实例。那我们怎么获得呢?大家都知道创建对象的方式除了最常见的new的方式,还有就是靠反射获得对象。
反射获得Unsafe实例

private static Unsafe getUnsafeinstance() throws Exception {
    Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafeField.setAccessible(true);
    return (Unsafe) theUnsafeField.get(null);
}

这样我们就获得了Unsafe的实例,就能够自己进行内存操作了
举例:操作数组
在介绍方法之前:先说一下Arrays和其他的java对象都有对象的头信息,存储在实际数据的最前面。arrayBaseoffset:返回数组第一个元素地址相对于数组起始地址的偏移量(也就是对象头的长度)。数组中元素的大小通过arrayIndexScale获得,即第n+1个元素的开始位置应该是 :arrayOffset + n * arrayScale

Unsafe unsafe = getUnsafeInstance();
int[] intArray = {1, 2, 3, 4, 5};
int offSet = unsafe.arrayBaseOffset(int[].class);
int scale = unsafe.arrayIndexScale(int[].class);
// 改变数组中 第三个元素的值
unsafe.putInt(intArray, (long) offSet + scale * 2, 10);
for (int i = 0; i < intArray.length; i++) {
    int value = unsafe.getInt(intArray, (long)offSet + scale * i);
    System.out.print(value + " ");
}  

同样,Unsafe也能对java对象、字符串进行类似操作。同时Unsafe也提供了cas操作,来解决并发问题,在这里就不详细叙述了。
那我们在看一下disruptor对Unsafe的使用

private static final Unsafe THE_UNSAFE;

static {
    try {
        final PrivilegedExceptionAction<Unsafe> action = new PrivilegedExceptionAction<Unsafe>() {
            public Unsafe run() throws Exception {
                Filed theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
                theUnsafe.setAccessible(true);
                return (Unsafe) theUnsafe.get(null);
            }
        };
        THE_UNSAFE = AccessController.doPrivileged(action);
    } catch (Exception e) {
        throw new RuntimeException("Unsafe to load unsafe", e);
    }
}

我们看到disruptor就是通过反射来创建Unsafe实例的,但是又遇到了AccessController,看名字我们就知道这是权限相关,下一节我们总结一下权限相关的知识

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值