Java 知识-- Unsafe

本文最后更新于:4 个月前

关于 unsafe

  1. 不能使用提供的 getInstance() 方法获取单例对象

    unsafe 是单例的

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        // 此处做了限制,非根类加载器 bootstrap class loader 调用会报错
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }
  2. 要想获得 unsafe 实例

    1. 反射

      private static Unsafe reflectGetUnsafe() {
          try {
              Field field = Unsafe.class.getDeclaredField("theUnsafe");
              field.setAccessible(true);
              return (Unsafe) field.get(null);
          } catch (Exception e) {
          log.error(e.getMessage(), e);
          return null;
          }
      }
    2. 修改 JVM 参数,把调用 Unsafe 相关方法的类 A 所在 jar 包路径追加到默认的 bootstrap 路径中,使得 A 被引导类加载器加载,从而通过 Unsafe.getUnsafe 方法安全的获取 Unsafe 实例。

      java -Xbootclasspath/a: ${path}
      # 其中path为调用Unsafe相关方法的类所在jar包路径

CAS 相关

// var2 字段值增加 var4
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    // 自旋 + CAS
    do {
        // 获取当前对象 var1 中 var2 字段的地址值
        // 即从内存中获取字段的真实值
        var5 = this.getIntVolatile(var1, var2);
        // 如果 CAS 失败,那么重试
        // 如果成功,跳出循环
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}

var2 这个偏移量是通过:

valueOffset = unsafe.objectFieldOffset(TargetObject.class.getDeclaredField("fileName"));

对于任意一个类要修改的字段,可以通过这种方式传入类名和字段名来获取对象字段的内存地址