Unsafe 概述

A collection of methods for performing low-level, unsafe operations. Although the class and all methods are public, use of this class is limited because only trusted code can obtain instances of it.

Unsafe 是一个用来执行低层次(native级别),不安全的操作的方法集合。尽管该类和其方法都是 public 的,但该类的使用受到限制,因为只有受信任的代码才能获得它的实例。

为什么说这个类的使用受到限制呢?看一下该类的构造器:

  1. public final class Unsafe {
  2. private Unsafe() {}
  3. private static final Unsafe theUnsafe = new Unsafe();
  4. /**
  5. * 为调用者提供执行不安全操作的能力。
  6. *
  7. * 调用者应该小心保护返回的 Unsafe 对象,因为它可用于在任意内存地址读取和写入数据。 它绝不能
  8. * 传递给不受信任的代码。
  9. *
  10. * 此类中的大多数方法都非常low-level,并且对应于少量硬件指令(在特定机器上)。 因此建议编译器
  11. * 相应地优化这些方法。
  12. *
  13. * 这是使用不安全操作的建议习惯用法:
  14. *
  15. * class MyTrustedClass {
  16. * private static final Unsafe unsafe = Unsafe.getUnsafe();
  17. * ...
  18. * private long myCountAddress = ...;
  19. * public int getCount() { return unsafe.getByte(myCountAddress); }
  20. * }
  21. *
  22. * 它可以帮助编译器使局部变量成为final 变量
  23. *
  24. * @exception SecurityException if a security manager exists and its
  25. * <code>checkPropertiesAccess</code> method doesn't allow
  26. * access to the system properties.
  27. */
  28. @CallerSensitive
  29. public static Unsafe getUnsafe() {
  30. Class<?> caller = Reflection.getCallerClass();
  31. if (!VM.isSystemDomainLoader(caller.getClassLoader()))
  32. throw new SecurityException("Unsafe");
  33. return theUnsafe;
  34. }
  35. }
  1. Reflection 是一个 java.lang 和 java.lang.reflect 使用的通用实用程序。Reflection.getCallerClass() 返回的是调用了该方法的调用者的 class。
  2. VM.isSystemDomainLoader(caller.getClassLoader()) - 如果给定的类是由系统类加载器(BootstrapClassLoader)加载的 ,则返回 true。其具体实现如下:
    1. public static boolean isSystemDomainLoader(ClassLoader loader) {
    2. return loader == null; // 启动类加载器,BootstrapClassLoader
    3. }
    由上可以得知,Unsafe 提供的 getUnsafe() 方法只能被由系统类加载器加载的类去调用,否则就会出现异常。

获取 Unsafe 实例

那么,怎样获取到 Unsafe 的实例呢?这里有两种办法:

  1. 通过反射获取 theUnsafe 字段

    1. Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
    2. theUnsafeField.setAccessible(true);
    3. Object theUnsafe = theUnsafeField.get(null);
  2. 将要调用 getUnsafe() 类所在就 jar 包追加到由系统类加载器加载的类的路径中

    1. java -Xbootclasspath/a:${path} // path为需要调用 getUnsafe() 方法的类所在的 jar 包路径

Unsafe 中的一些重要方法

long objectFieldOffset(Field field)

  1. public native long objectFieldOffset(Field f);

返回指定变量在所属类中的内存偏移地址,该地址仅仅在该 Unsafe 函数中访问指定字段时使用。例如,在 AtomicInteger 中使用 Unsafe 类来获取 value 变量在 AtomicInteger 中的内存偏移量:

  1. static {
  2. try {
  3. valueOffset = unsafe.objectFieldOffset
  4. (AtomicInteger.class.getDeclaredField("value"));
  5. } catch (Exception ex) { throw new Error(ex); }
  6. }

int arrayBaseOffset(Class arrayClass)

获取数组中第一个元素的地址

  1. public class Unimportant {
  2. private int[] array = new int[]{1, 2, 3, 4};
  3. public static void main(String[] args) throws Exception {
  4. Unimportant unimportant = new Unimportant();
  5. Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
  6. theUnsafeField.setAccessible(true);
  7. Unsafe theUnsafe = (Unsafe) theUnsafeField.get(null);
  8. Field arrayField = unimportant.getClass().getDeclaredField("array");
  9. arrayField.setAccessible(true);
  10. int[] array = (int[]) arrayField.get(unimportant);
  11. System.out.println(theUnsafe.arrayBaseOffset(array.getClass())); // 16
  12. System.out.println(theUnsafe.arrayIndexScale(array.getClass())); // 4
  13. }
  14. }

int arrayIndexScale(Class arrayClass)

获取数组中一个元素占用的字节

boolean compareAndSwapLong(Object obj, long offset, longexpect, long update)

比较对象 obj 中偏移量为 offset 的变量的值是否与 expect 相等,相等则使用 update 值更新,然后返回 true,否则返回 false。相同的还有 compareAndSwapIntcompareAndSwapObject 等。

public native Object getObjectVolatile(Object obj, long offset)

从对象 obj 中偏移量为 offset 的位置获取变量的引用,使用 volatile 的加载语义。相同的还有 getIntVolatilegetBooleanVolatilegetByteVolatilegetShortVolatilegetCharVolatilegetLongVolatilegetFloatVolatilegetDoubleVolatile等。

void putObjectVolatile(Object obj, long offset, Object value)

在对象 obj 中偏移量为 offset 的位置设置值 value,使用 volatile 的加载语义。相同的还有 putIntVolatileputBooleanVolatileputByteVolatileputShortVolatileputCharVolatileputLongVolatileputFloatVolatileputDoubleVolatile等。

public native void putOrderedObject(Object o, long offset, Object x)

在对象 obj 中偏移量为 offset 的位置设置值 x,该方法并不保证修改后的值立即对其他线程可见。只有在变量使用volatile修饰并且预计会被意外修改时才使用该方法。相同的还有 putOrderedIntputOrderedLong等。

public native void park(boolean isAbsolute, long time)

阻塞当前线程。

  1. isAbsolute 等于 false
    1. time 等于 0 表示一直阻塞
    2. time 大于 0 表示等待指定的 time 时间后阻塞线程会被唤醒,这个 time 是个相对值,也就是相对当前时间累加 time 后当前线程就会被唤醒
  2. isAbsolute 等于 true
    1. time 大于 0 表示阻塞的线程到指定的时间点后会被唤醒,这里 time 是个绝对时间,是将某个时间点换算为 ms 后的值
  3. 当其他线程调用了当前阻塞线程的 interrupt 方法而中断了当前线程时,当前线程也会返回
  4. 其他线程调用了 unPark 方法并且把当前线程作为参数时当前线程也会返回

public native void unpark(Object thread)

唤醒调用park后阻塞的线程。

public final Object getAndSetObject(Object o, long offset, Object newValue)

获取对象 obj 中偏移量为 offset 的变量 volatile 语义的当前值,并设置变量 volatile 语义的值为 update。返回值是该内存地址的上一个保存的值。相同的还有 getAndSetIntgetAndSetLong 等。

public final int getAndAddInt(Object o, long offset, int delta)

获取对象 obj 中偏移量为 offset 的变量 volatile 语义的当前值,并设置变量值为原始值+addValue 。返回值是该内存地址的上一个保存的值。相同的还有 getAndAddLong 方法。

public native void loadFence()

内存屏障,禁止 load 操作重排序。屏障前的 load 操作不能被重排序到屏障后,屏障后的 load 操作不能被重排序到屏障前。

public native void storeFence()

存屏障,禁止 store 操作重排序。屏障前的 store 操作不能被重排序到屏障后,屏障后的 store 操作不能被重排序到屏障前。

public native void fullFence()

存屏障,禁止 load、store 操作重排序。

参考

/src/share/classes/sun/misc/Unsafe.java

/src/share/classes/sun/reflect/Reflection.java

/src/share/classes/sun/misc/VM.java

Java魔法类:Unsafe应用解析