TOP 带着问题看源码
如何获取 Unsafe 实例
如何利用 Unsafe API 绕开 JVM的控制
CAS 到底是什么
Unsafe 中的线程调度是怎么回事
1. 基本介绍 Unsafe 是用于在实质上扩展 Java 语言表达能力、便于在 Java 代码里实现原本要在 C 层实现的核心库功能用的。这些功能包括裸内存的申请、释放、访问,低层硬件的 atomic/volatile 支持,创建未初始化对象等。但由于 Unsafe 类使 Java 语言拥有不应该暴露的骚操作,增加了程序出问题的风险。
1.1 获取 Unsafe 实例 1 2 3 4 5 6 7 8 9 10 11 public class UnsafeTest { private static Unsafe unsafe; public static void main (String[] args) throws Exception { Class c = UnsafeTest.class.getClassLoader().loadClass("sun.misc.Unsafe" ); Field f = c.getDeclaredField("theUnsafe" ); f.setAccessible(true ); unsafe = (Unsafe)f.get(c); unsafe.xx(); } }
回到 TOP 1 可以明白,通过反射获取 unsafe 实例。
2. 功能介绍
3. 数组相关 1 2 3 4 public native int arrayBaseOffset (Class<?> var1) ;public native int arrayIndexScale (Class<?> var1) ;
通过定位数组第一个元素的偏移地址和每个元素占用的大小。
例如第一个元素偏移地址是16,存的是 int 类型,则可以通过要查询的 index * 4 + 16 来获取到对应的值。
我们可以在 AtomicIntegerArray 中看到这些操作,不过作者巧妙的通过位运算来计算index对应的偏移量。
1 2 3 4 5 6 7 8 9 10 private static final int base = unsafe.arrayBaseOffset(int [].class);int scale = unsafe.arrayIndexScale(int [].class);shift = 31 - Integer.numberOfLeadingZeros(scale); offset = index << shift + base; unsafe.getIntVolatile(array, offset);
4. 内存屏障 1 2 3 4 5 6 public native void loadFence () ;public native void storeFence () ;public native void fullFence () ;
内存屏障主要是避免 CPU 或者 编译器对代码重排序。
如并发包中 StampedLock 解决因代码重排序校验不准确,采用loadFence()。
1 2 3 4 public boolean validate (long stamp) { U.loadFence(); return (stamp & SBITS) == (state & SBITS); }
5. 系统相关 1 2 3 4 public native int addressSize () ;public native int pageSize () ;
可以根据内存页大小计算分配页数
6. 线程调度 1 2 3 4 5 6 7 8 9 10 public native void unpark (Object var1) ;public native void park (boolean var1, long var2) ;public native void monitorEnter (Object var1) ;public native void monitorExit (Object var1) ;public native boolean tryMonitorEnter (Object var1) ;
大名鼎鼎的 AQS 就是通过 park、unpark 来对线程阻塞和唤醒的
回到 TOP 4 可以明白其实就是 park unpark
7. 内存操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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) ;public native byte getXx (long var1) ;public native void putXx (long var1, xx var3) ;
以上的内存操作针对的都是堆外内存操作,与我们平时自己创建的对象都在堆内不同,堆外不会受到 JVM 内存管理,合理使用可以减少原本堆内内存使 GC 时间减少。
java.nio.DirectByteBuffer
中利用了堆外内存减少堆内堆外的copy
回到 TOP 2 可以明白使用堆外内存操作可以绕开 JVM 控制
8. CAS(Compare And Swap, 比较和替换) 1 2 // 根据第二个参数”偏移量”去拿偏移量这么多的属性的值和第三个参数对比,如果相同则将该属性值替换为第四个参数。该偏移量是指某个字段相对Java对象的起始位置的偏移量,可以通过unsafe.objectFieldOffset(param)去获取对应属性的偏移量。 public final native boolean compareAndSwapXx(Object var1, long var2, Xx var4, Xx var5);
CAS 是一条 CPU 的原子指令(cmpxchg),如果是多核处理器会加上 LOCK 前缀
1 2 3 4 5 6 7 8 inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { int mp = os::is_MP (); __asm__ volatile (LOCK_IF_MP(%4 ) "cmpxchgl %1,(%3)" : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) : "cc" , "memory" ) ; return exchange_value; }
CAS 在并发包中被广泛应用,回到 TOP 3 可以明白 CAS 是一条 CPU 的原子指令。
9. Class 相关 1 2 3 4 5 6 7 8 9 10 11 12 public native long staticFieldOffset (Field var1) ;public native Object staticFieldBase (Field var1) ;public native boolean shouldBeInitialized (Class<?> var1) ;public native void ensureClassInitialized (Class<?> var1) ;public native Class<?> defineClass(String var1, byte [] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);public native Class<?> defineAnonymousClass(Class<?> var1, byte [] var2, Object[] var3);
10. 对象操作 1 2 3 4 5 6 7 8 9 10 public native long objectFieldOffset (Field var1) ;public native Object getObjectVolatile (Object o, long offset) ;public native void putObjectVolatile (Object o, long offset, Object x) ;public native void putOrderedObject (Object o, long offset, Object x) ;public native Object allocateInstance (Class<?> cls) throws InstantiationException ;