深入理解sun.misc.Unsafe原理
前言
Unsafe类在JDK源码中被广泛使用,在Spark使用off-heap memory时也会使用到,该类功能很强大,涉及到类加载机制(深入理解ClassLoader工作机制),其实例一般情况是获取不到的,源码中的设计是采用单例模式,不是系统加载初始化就会抛出SecurityException异常。
这个类的提供了一些绕开JVM的更底层功能,基于它的实现可以提高效率。但是,它是一把双刃剑:正如它的名字所预示的那样,它是Unsafe的,它所分配的内存需要手动free(不被GC回收)。如果对Unsafe类理解的不够透彻,就进行使用的话,就等于给自己挖了无形之坑,最为致命。
由于sun并没有将其开源,也没给出官方的Document,所以笔者只能参考一些博客(如Java Magic. Part 4: sun.misc.Unsafe)以及Unsafe在JDK源码中的一些使用,来加深理解。
获取Unsafe类的实例
- 必须是Bootstrap ClassLoader加载的类
getUnsafe方法源码:![在这里插入图片描述]()
isSystemDomainLoader:![在这里插入图片描述]()
- 通过反射暴力获取
// 通过反射实例化Unsafe
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
Unsafe类中的核心方法
//重新分配内存
public native long reallocateMemory(long address, long bytes);
//分配内存
public native long allocateMemory(long bytes);
//释放内存
public native void freeMemory(long address);
//在给定的内存块中设置值
public native void setMemory(Object o, long offset, long bytes, byte value);
//从一个内存块拷贝到另一个内存块
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
//获取值,不管java的访问限制,其他有类似的getInt,getDouble,getLong,getChar等等
public native Object getObject(Object o, long offset);
//设置值,不管java的访问限制,其他有类似的putInt,putDouble,putLong,putChar等等
public native void putObject(Object o, long offset);
//从一个给定的内存地址获取本地指针,如果不是allocateMemory方法的,结果将不确定
public native long getAddress(long address);
//存储一个本地指针到一个给定的内存地址,如果地址不是allocateMemory方法的,结果将不确定
public native void putAddress(long address, long x);
//该方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的
public native long staticFieldOffset(Field f);
//报告一个给定的字段的位置,不管这个字段是private,public还是保护类型,和staticFieldBase结合使用
public native long objectFieldOffset(Field f);
//获取一个给定字段的位置
public native Object staticFieldBase(Field f);
//确保给定class被初始化,这往往需要结合基类的静态域(field)
public native void ensureClassInitialized(Class c);
//可以获取数组第一个元素的偏移地址
public native int arrayBaseOffset(Class arrayClass);
//可以获取数组的转换因子,也就是数组中元素的增量地址。将arrayBaseOffset与arrayIndexScale配合使用, 可以定位数组中每个元素在内存中的位置
public native int arrayIndexScale(Class arrayClass);
//获取本机内存的页数,这个值永远都是2的幂次方
public native int pageSize();
//告诉虚拟机定义了一个没有安全检查的类,默认情况下这个类加载器和保护域来着调用者类
public native Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
//定义一个类,但是不让它知道类加载器和系统字典
public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches);
//锁定对象,必须是没有被锁的
public native void monitorEnter(Object o);
//解锁对象
public native void monitorExit(Object o);
//试图锁定对象,返回true或false是否锁定成功,如果锁定,必须用monitorExit解锁
public native boolean tryMonitorEnter(Object o);
//引发异常,没有通知
public native void throwException(Throwable ee);
//CAS,如果对象偏移量上的值=期待值,更新为x,返回true.否则false.类似的有compareAndSwapInt,compareAndSwapLong,compareAndSwapBoolean,compareAndSwapChar等等。
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);
// 该方法获取对象中offset偏移地址对应的整型field的值,支持volatile load语义。类似的方法有getIntVolatile,getBooleanVolatile等等
public native Object getObjectVolatile(Object o, long offset);
//线程调用该方法,线程将一直阻塞直到超时,或者是中断条件出现。
public native


