Java和C++语言的一个重要区别就是Java中我们无法直接操作一块内存区域,不能像C++中那样可以自己申请内存和释放内存。Java中的Unsafe类为我们提供了类似C++手动管理内存的能力。
1 | public class RevisedObjectInHeap |
主要功能
Unsafe的功能如下图:
普通读写
通过Unsafe可以读写一个类的属性,即使这个属性是私有的,也可以对这个属性进行读写。
读写一个Object属性的相关方法
1 | public native int getInt(Object var1, long var2); |
getInt用于从对象的指定偏移地址处读取一个int。putInt用于在对象指定偏移地址处写入一个int。其他的primitive type也有对应的方法。
Unsafe还可以直接在一个地址上读写
1 | public native byte getByte(long var1); |
getByte用于从指定内存地址处开始读取一个byte。putByte用于从指定内存地址写入一个byte。其他的primitive type也有对应的方法。
volatile读写
普通的读写无法保证可见性和有序性,而volatile读写就可以保证可见性和有序性。
1 | public native int getIntVolatile(Object var1, long var2); |
getIntVolatile方法用于在对象指定偏移地址处volatile读取一个int。putIntVolatile方法用于在对象指定偏移地址处volatile写入一个int。
volatile读写相对普通读写是更加昂贵的,因为需要保证可见性和有序性,而与volatile写入相比putOrderedXX写入代价相对较低,putOrderedXX写入不保证可见性,但是保证有序性,所谓有序性,就是保证指令不会重排序。
有序写入
有序写入只保证写入的有序性,不保证可见性,就是说一个线程的写入不保证其他线程立马可见。
1 | public native void putOrderedObject(Object var1, long var2, Object var4); |
直接内存操作
我们都知道Java不可以直接对内存进行操作,对象内存的分配和回收都是由JVM帮助我们实现的。但是Unsafe为我们在Java中提供了直接操作内存的能力。
1 | // 分配内存 |
CAS相关
JUC中大量运用了CAS操作,可以说CAS操作是JUC的基础,因此CAS操作是非常重要的。Unsafe中提供了int,long和Object的CAS操作:
1 | public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); |
CAS一般用于乐观锁,它在Java中有广泛的应用,ConcurrentHashMap,ConcurrentLinkedQueue中都有用到CAS来实现乐观锁。
偏移量相关
1 | public native long staticFieldOffset(Field var1); |
staticFieldOffset方法用于获取静态属性Field在对象中的偏移量,读写静态属性时必须获取其偏移量。objectFieldOffset方法用于获取非静态属性Field在对象实例中的偏移量,读写对象的非静态属性时会用到这个偏移量。staticFieldBase方法用于返回Field所在的对象。arrayBaseOffset方法用于返回数组中第一个元素实际地址相对整个数组对象的地址的偏移量。arrayIndexScale方法用于计算数组中第一个元素所占用的内存空间。
线程调度
1 | public native void unpark(Object var1); |
park方法和unpark方法相信看过LockSupport类的都不会陌生,这两个方法主要用来挂起和唤醒线程。LockSupport中的park和unpark方法正是通过Unsafe来实现的:
1 | // 挂起线程 |
monitorEnter方法和monitorExit方法用于加锁,Java中的synchronized锁就是通过这两个指令来实现的。
类加载
1 | public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); |
defineClass方法定义一个类,用于动态地创建类。 defineAnonymousClass用于动态的创建一个匿名内部类。 allocateInstance方法用于创建一个类的实例,但是不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类。 shouldBeInitialized方法用于判断是否需要初始化一个类。 ensureClassInitialized方法用于保证已经初始化过一个类。
内存屏障
1 | public native void loadFence(); |
loadFence:保证在这个屏障之前的所有读操作都已经完成。 storeFence:保证在这个屏障之前的所有写操作都已经完成。 fullFence:保证在这个屏障之前的所有读写操作都已经完成。