Java中的final很重要,final关键字可用来修饰类,方法,变量。修饰类时表示该类不可被继承。修饰方法时表示该方法不可被重载。修饰变量时表示不可变值或引用。JSR-133通过为final域增加写和读重排序规则,为Java程序提供初始化安全保障:只要对象是正确构造的(没有this逃逸),那么就不需要使用同步。任意线程都能看到这个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"); } } }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变量情况有点复杂,涉及到Java内存模型。由于final变量的不可变性,编译器和处理器在对指令序列重排优化时需要借助内存屏障来保证多线程下JMM中final语义的一致性。因此编译器和处理器在对指令重排优化时需要遵循两个原则:
在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个变量,这两个操作之间不能重排。处理读一个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的写写屏障语义。