问题
JVM可以信任非静态的final字段吗?
基础知识
编译器通常信任static final字段,因为已知该值不依赖于特定对象,并且已知它不会改变。那对于静态常量实例的final字段也使如此吗?
class M {final int x;M(int x) { this.x = x; }
}static final M KNOWN_M = new M(1337);void work() {// We know exactly the slot that holds the variable, can we just// inline the value 1337 here?return KNOWN_M.x;
}
Java 言规范允许不看到这样的更新,因为字段是final 。不幸的是,真正的框架设法依赖于更强大的行为:字段更新将被看到。正在进行的实验积极优化这些情况,并在实际写入发生时取消优化。当前状态是一些内部类是隐式信任的,下面是OpenJDK的源码:
static bool trust_final_non_static_fields(ciInstanceKlass* holder) {if (holder == NULL)return false;if (holder->name() == ciSymbol::java_lang_System())// Never trust strangely unstable finals: System.out, etc.return false;// Even if general trusting is disabled, trust system-built closures in these packages.if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke"))return true;// Trust VM anonymous classes. They are private API (sun.misc.Unsafe) and can't be serialized,// so there is no hacking of finals going on with them.if (holder->is_anonymous())return true;// Trust final fields in all boxed classesif (holder->is_box_klass())return true;// Trust final fields in Stringif (holder->name() == ciSymbol::java_lang_String())return true;// Trust Atomic*FieldUpdaters: they are very important for performance, and make up one// more reason not to use Unsafe, if their final fields are trusted. See more in JDK-8140483.if (holder->name() == ciSymbol::java_util_concurrent_atomic_AtomicIntegerFieldUpdater_Impl() ||holder->name() == ciSymbol::java_util_concurrent_atomic_AtomicLongFieldUpdater_CASUpdater() ||holder->name() == ciSymbol::java_util_concurrent_atomic_AtomicLongFieldUpdater_LockedUpdater() ||holder->name() == ciSymbol::java_util_concurrent_atomic_AtomicReferenceFieldUpdater_Impl()) {return true;}return TrustFinalNonStaticFields;
}
并且,只有在提供实验性的 -XX:+TrustFinalNonStaticFields运行参数时,常规的final字段才会受到信任。
实验
源码
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class TrustFinalFields {static final T t_static_final;static T t_static;final T t_inst_final;T t_inst;static {t_static_final = new T(1000);t_static = new T(1000);}{t_inst_final = new T(1000);t_inst = new T(1000);}static class T {final int x;public T(int x) {this.x = x;}}@Benchmark public int _static_final() { return 1000 / t_static_final.x; }@Benchmark public int _static() { return 1000 / t_static.x; }@Benchmark public int _inst_final() { return 1000 / t_inst_final.x; }@Benchmark public int _inst() { return 1000 / t_inst.x; }}
运行结果
Benchmark Mode Cnt Score Error Units
TrustFinalFields._inst avgt 15 4.316 ± 0.003 ns/op
TrustFinalFields._inst_final avgt 15 4.317 ± 0.002 ns/op
TrustFinalFields._static avgt 15 4.282 ± 0.011 ns/op
TrustFinalFields._static_final avgt 15 4.202 ± 0.002 ns/op
汇编代码如下:
0.02% ↗ movabs $0x782b67520,%r10 ; {oop(a 'org/openjdk/TrustFinalFields$T';)}│ mov 0x10(%r10),%r10d ; get field $x│ ...
0.19% │ cltd
0.02% │ idiv %r10d ; idiv│ ...
0.16% │ test %r11d,%r11d ; check and run @Benchmark again╰ je BACK
对象本身被信任位于堆中的给定位置( $0x782b67520 ),但我们不信任该字段!使用 -XX:+TrustFinalNonStaticFields 运行相同操作会产生以下结果:
Benchmark Mode Cnt Score Error Units
TrustFinalFields._inst avgt 15 4.318 ± 0.001 ns/op
TrustFinalFields._inst_final avgt 15 4.317 ± 0.003 ns/op
TrustFinalFields._static avgt 15 4.290 ± 0.002 ns/op
TrustFinalFields._static_final avgt 15 1.901 ± 0.001 ns/op # <--- !!!
从上述的执行结果可以看出,这里final一个字段被折叠了,如 perfasm 输出所示:
3.04% ↗ mov %r10,(%rsp)│ mov 0x38(%rsp),%rsi
8.26% │ mov $0x1,%edx ; <--- constant folded to 1│ ...
0.04% │ test %r11d,%r11d ; check and run @Benchmark again╰ je BACK
总结
信任实例最终字段需要了解我们正在操作的对象。因此,至少对于已知的系统类而言。通过这些最终字段进行常量折叠是MethodHandle -s、 VarHandle -s`、 Atomic*FieldUpdaters和核心库中其他高性能实现的性能基础。应用程序可能会尝试使用实验性的 VM 选项,但行为不当的应用程序可能造成的破坏可能会得不偿失。