前几天下班回家的路上一个客户端同事问我,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,看名字我们就知道这是权限相关,下一节我们总结一下权限相关的知识