HotSpot final原理

Java中的final很重要,final关键字可用来修饰类,方法,变量。修饰类时表示该类不可被继承。修饰方法时表示该方法不可被重载。修饰变量时表示不可变值或引用。JSR-133通过为final域增加写和读重排序规则,为Java程序提供初始化安全保障:只要对象是正确构造的(没有this逃逸),那么就不需要使用同步。任意线程都能看到这个final域在构造函数中被初始化后的值。

一.final类

在HotSpot中对final类,在类加载阶段验证其是否继承了final类。

hotspot/src/share/vm/classfile/classFileParser.cpp

void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const stream ...) {
  ...
  if (_super_klass != NULL) {
    if (_super_klass->is_final()) {
      //如果继承了一个final类则抛出异常
      THROW_MSG(vmSymbols::java_lang_VerifyError(), "Cannot inherit from final class");
    }
  }
}

二.final方法

HotSpot中对final方法,在类加载阶段校验是否对父类final方法重写

hotspot/src/share/vm/classfile/classFileParser.cpp

static void check_final_method_override(const InstanceKlass* this_klass, TRAPS) {
  //当前类的所有方法
  const Array<Method*>* const methods = this_klass->methods();
  const int num_methods = methods->length();
  //检查当前类的所有方法是否是对父类final方法的重载
  for (int index = 0; index < num_methods; index++) {
    const Method* const m = methods->at(index);
    //跳过私有,静态,<init>方法
    if ((!m->is_private() && !m->is_static()) &&
        (m->name() != vmSymbols::object_initializer_name())) {
      const Symbol* const name = m->name();
      const Symbol* const signature = m->signature();
      //父类
      const Klass* k = this_klass->super();
      const Method* super_m = NULL;
      while (k != NULL) {
        // final方法
        if (k->has_final_method()) {
          //使用类签名查找
          super_m = InstanceKlass::cast(k)->lookup_method(name, signature);
          if (super_m == NULL) {
            break; // didn't find any match; get out
          }
          //匹配如果存在重载final方法的情况直接抛出异常
          if (super_m->is_final() && !super_m->is_static() &&
              // matching method in super is final, and not static
              (Reflection::verify_field_access(this_klass,
                                               super_m->method_holder(),
                                               super_m->method_holder(),
                                               super_m->access_flags(), false))
            // this class can access super final method and therefore override
            ) {
            Exceptions::fthrow(
              THREAD_AND_LOCATION,
              vmSymbols::java_lang_VerifyError(),
              "class %s overrides final method %s.%s%s",
              this_klass->external_name(),
              super_m->method_holder()->external_name(),
              name->as_C_string(),
              signature->as_C_string()
            );
            return;
          }
          // 爷爷类
          k = super_m->method_holder()->super();
          continue;
        }
        k = k->super();
      }
    }
  }
}

三.final变量

对于final变量情况有点复杂,涉及到Java内存模型。由于final变量的不可变性,编译器和处理器在对指令序列重排优化时需要借助内存屏障来保证多线程下JMM中final语义的一致性。因此编译器和处理器在对指令重排优化时需要遵循两个原则:

  1. 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个变量,这两个操作之间不能重排。
  2. 处理读一个final域的对象引用,与随后读取这个final域,这两个操作之间不能重排。
public class FinalField {

  public int i;  //普通变量
  public final int j;  //final变量

  static FinalField obj;

  public FinalField(){  //构造函数
      i = 1;  //写普通字段
      j = 2;  //写final字段
  }

  public static void writer(){  //线程A
        obj = new FinalField();
  }

  public static void reader(){  //线程B
      FinalField object  = obj;  //对象引用
      int a = object.i;  //读普通字段
      int b = object.j;  //读final字段
  }

}

构造函数中写final字段的重排序规则禁止把final字段的写重排到构造函数外。JMM禁止编译器把final写指令重排到构造函数外,编译器在final写之后,return之前插入一个StoreStore屏障来禁止重排。

在这里插入图片描述
假设A,B线程的执行序列如上图,普通变量i被重排到构造函数之外,线程B读取到的i并未经过构造函数初始化。相反final变量j被内存屏障限制在构造函数中,被正确初始化读取了。

在一个线程中,初次读对象引用与触底读该对象包含的final字段,JMM禁止处理器重排这两个操作,会在两者之间插入LoadLoad屏障。编译器不会破坏两个操作的依赖关系,编译器不会对其重排。

在这里插入图片描述
JMM保证读final域操作中,读obj和读obj.j两个操作不会被处理器重排序。

使用javap -v FinalField.class 查看字节码,变量j的flags多了ACC_FINAL标志

{
  public int i;
    descriptor: I
    flags: (0x0001) ACC_PUBLIC

  public final int j;
    descriptor: I
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL     //final变量

  static com.oak.app.vo.FinalField obj;
    descriptor: Lcom/oak/app/vo/FinalField;
    flags: (0x0008) ACC_STATIC

  public com.oak.app.vo.FinalField();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_1
         6: putfield      #2                  // Field i:I
         9: aload_0
        10: iconst_2
        11: putfield      #3                  // Field j:I
        14: return
      LineNumberTable:
        line 10: 0
        line 11: 4
        line 12: 9
        line 13: 14

  public static void writer();
    descriptor: ()V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: new           #4                  // class com/oak/app/vo/FinalField
         3: dup
         4: invokespecial #5                  // Method "<init>":()V
         7: putstatic     #6                  // Field obj:Lcom/oak/app/vo/FinalField;
        10: return
      LineNumberTable:
        line 16: 0
        line 17: 10

  public static void reader();
    descriptor: ()V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=0
         0: getstatic     #6                  // Field obj:Lcom/oak/app/vo/FinalField;
         3: astore_0
         4: aload_0
         5: getfield      #2                  // Field i:I
         8: istore_1
         9: aload_0
        10: getfield      #3                  // Field j:I
        13: istore_2
        14: return
      LineNumberTable:
        line 20: 0
        line 21: 4
        line 22: 9
        line 23: 14
}

变量的写和读对应模板表的putfield和getfiled,在X86处理器上不会对写写操作重排序,也不会对间接依赖关系做重排序,所以TemplateTable::putfield和TemplateTable::getfield中没有对flags标志ACC_FINAL做检查,自然没有屏障插入操作。但是ARM处理器平台对写final插入了StoreStore屏障,对读final(间接依赖关系)没有屏障,如下:

hotspot/src/cpu/arm/vm/templateTable_arm.cpp

void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteControl rc) {
   if (gen_volatile_check) {
    Label notVolatile;
    if (is_static) { //静态字段,随类初始化处理
      //检查volatile
      __ tbz(Rflagsav, ConstantPoolCacheEntry::is_volatile_shift, notVolatile);
      volatile_barrier(MacroAssembler::StoreLoad, Rtemp);
      __ bind(notVolatile);
    } else { //非静态
      // Check for volatile field and final field
      Label skipMembar;
      //检查volatile和final
      __ tst(Rflagsav, 1 << ConstantPoolCacheEntry::is_volatile_shift |
                       1 << ConstantPoolCacheEntry::is_final_shift);
      __ b(skipMembar, eq);
      __ tbz(Rflagsav, ConstantPoolCacheEntry::is_volatile_shift, notVolatile);

      // volatile字段写后插入StoreLoad屏障
      volatile_barrier(MacroAssembler::StoreLoad, Rtemp);
      __ b(skipMembar);

      // final字段写后面插入StoreStore屏障
      __ bind(notVolatile);
      volatile_barrier(MacroAssembler::StoreStore, Rtemp);

      __ bind(skipMembar);
    }
}

hotspot/src/cpu/arm/vm/templateTable_arm.cpp

void TemplateTable::volatile_barrier(MacroAssembler::Membar_mask_bits order_constraint,
                                     Register tmp,
                                     bool preserve_flags,
                                     Register load_tgt) {
#ifdef AARCH64
  __ membar(order_constraint);
#else
  __ membar(order_constraint, tmp, preserve_flags, load_tgt);
#endif
}

hotspot/src/cpu/arm/vm/macroAssembler_arm.cpp

void MacroAssembler::membar(Membar_mask_bits order_constraint,
                            Register tmp,
                            bool preserve_flags,
                            Register load_tgt) {
  if (!os::is_MP()) return;

  if (order_constraint == StoreStore) { //对于StoreStore屏障,执行ARM处理器的DMB指令
    dmb(DMB_st, tmp);
  } else if ((order_constraint & StoreLoad)  ||
             (order_constraint & LoadLoad)   ||
             (order_constraint & StoreStore) ||
             (load_tgt == noreg)             ||
             preserve_flags) {
    dmb(DMB_all, tmp);
  } else {
    Label not_taken;
    bind(not_taken);
    cmp(load_tgt, load_tgt);
    b(not_taken, ne);
  }
}

ARM处理器的DMB指令保证: 仅当所有在它前面的存储器访问操作都执行完毕后,才提交(commit)在它后面的存储器访问操作。显然它能保证final的写写屏障语义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值