参考自:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
CAS全程是Compare And Swap,即比较交换;其核心算法:执行函数CAS(V,E,N)
其中:
V代表要更新的变量
- E代表预期值
- N代表新值
算法:当V==E时,修改V=N;否则什么都不做。若V!=E即代表此变量在其他线程中进行了更新。
位于sun.misc包,主要用于执行低级别,不安全的操作方法
由源码可知,Unsafe为一个单实例类,调用getUnsafe时,当且仅当调用此方法的类为引导类加载器
所加载时才合法。
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
获取其实例的方法主要有2个:
通过Java命令行命令-Xbootclasspath/a
把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被引导类加载器加载,从而通过Unsafe.getUnsafe
方法安全的获取Unsafe实例。
java -Xbootclasspath/a: ${path} // 其中path为调用Unsafe相关方法的类所在jar包路径
通过反射获取
Field mFiled = Unsafe.class.getDeclaredField("theUnsafe");
mFiled.setAccessible(true);
Unsafe sInstance = (Unsafe) mFiled.get(null);
其API大致可以分为内存操作、CAS、Class相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
主要包括堆外内存分配、内存、拷贝、释放、内存块中设置值等
// 分配内存
public native long allocateMemory(long var1);
// 内存扩充
public native long reallocateMemory(long var1, long var3);
// 在给定内存块中设置值
public native void setMemory(Object var1, long var2, long var4, byte var6);
// 拷贝
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 释放
public native void freeMemory(long var1);
堆外内存:存在于JVM管控之外的内存区域,Java中对于其的操作依赖于Unsafe
使用堆外内存的原因:
对垃圾回收停顿的改善。
- 提升程序I/O操作的性能。
典型应用:DirectByteBuffer
/**
* CAS
* @param o 包含要修改field的对象
* @param offset 对象中某field的偏移量
* @param expected 期望值
* @param update 更新值
* @return true | false
*/
// 执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);
public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);
典型应用:java.util.concurrent.atomic相关类、Java AQS、CurrentHashMap等
主要包括线程挂起、恢复、锁机制等
// 取消阻塞线程
public native void unpark(Object thread);
// 阻塞线程
public native void park(boolean isAbsolute, long time);
// 获得对象锁(可重入锁)
/** @deprecated */
@Deprecated
public native void monitorEnter(Object var1);
// 释放对象锁
/** @deprecated */
@Deprecated
public native void monitorExit(Object var1);
//尝试获取对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);
典型应用:Java锁和同步器框架的核心类AbstractQueuedSynchronizer,就是通过调用LockSupport.park()
和LockSupport.unpark()
实现线程的阻塞和唤醒的,而LockSupport的park、unpark方法实际是调用Unsafe的park、unpark方式来实现
主要提供Class和它的静态字段的操作相关的方法,包括静态字段内存定位、定义类、匿名类;检验&确保类的初始化
//获取给定静态字段的内存地址偏移量,这个值对于给定的字段是唯一且固定不变的
public native long staticFieldOffset(Field f);
//获取一个静态类中给定字段的对象指针
public native Object staticFieldBase(Field f);
//判断是否需要初始化一个类,通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。 当且仅当ensureClassInitialized方法不生效时返回false。
public native boolean shouldBeInitialized(Class<?> c);
//检测给定的类是否已经初始化。通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。
public native void ensureClassInitialized(Class<?> c);
//定义一个类,此方法会跳过JVM的所有安全检查,默认情况下,ClassLoader(类加载器)和ProtectionDomain(保护域)实例来源于调用者
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 long objectFieldOffset(Field f);
//获得给定对象的指定地址偏移量的值,与此类似操作还有:getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//给定对象的指定地址偏移量设值,与此类似操作还有:putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
public native Object getObjectVolatile(Object o, long offset);
//存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
public native void putObjectVolatile(Object o, long offset, Object x);
//有序、延迟版本的putObjectVolatile方法,不保证值的改变被其他线程立即看到。只有在field被volatile修饰符修饰时有效
public native void putOrderedObject(Object o, long offset, Object x);
//绕过构造方法、初始化代码来创建对象
public native Object allocateInstance(Class<?> cls) throws InstantiationException;
典型应用:
常规对象实例化
非常规的实例化方式
Unsafe中提供allocateInstance方法,仅通过Class对象就可以创建此类的实例对象,而且不需要调用其构造函数、初始化代码、JVM安全检查等。它抑制修饰符检测,也就是即使构造器是private修饰的也能通过此方法实例化,只需提类对象即可创建相应的对象。由于这种特性,allocateInstance在java.lang.invoke、Objenesis(提供绕过类构造器的对象生成方式)、Gson(反序列化时用到)中都有相应的应用。
定位数组中每个元素在内存中的位置
//返回数组中第一个元素的偏移地址
public native int arrayBaseOffset(Class<?> arrayClass);
//返回数组中一个元素占用的大小
public native int arrayIndexScale(Class<?> arrayClass);
内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作,避免代码重排序。
//内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//内存屏障,禁止load、store操作重排序
public native void fullFence();
典型应用:
Java 8中引入了一种锁的新机制——StampedLock
包含2个获取系统信息的方法
//返回系统指针的大小。返回值为4(32位系统)或 8(64位系统)。
public native int addressSize();
//内存页的大小,此值为2的幂次方。
public native int pageSize();