我们知道,将字段设置为final通常是一个好主意,因为我们获得了线程安全性和不变性,这使得代码更容易推理。我很好奇是否有相关的性能成本。
Java 内存模型保证了这一点final Field Semantics http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5:
仅在对象完全初始化后才能看到对该对象的引用的线程保证看到该对象的最终字段的正确初始化值。
这意味着对于这样的课程
class X {
X(int a) {
this.a = a;
}
final int a;
static X instance;
}
每当线程 1 创建这样的实例时
X.instance = new X(43);
while (true) doSomethingEventuallyEvictingCache();
并且线程 2 看到了它
while (X.instance == null) {
doSomethingEventuallyEvictingCache();
}
System.out.println(X.instance.a);
它必须打印 43。没有final
修改器、JIT 或 CPU 可以对存储重新排序(第一个存储X.instance
然后设置a=43
)并且线程 2 可以看到默认初始化值并打印 0。
当 JIT 看到final
它显然避免重新排序。但它也必须强制CPU服从命令。是否存在相关的性能损失?
是否存在相关的性能损失?
如果你看一下JIT编译器的源代码,你会发现文件中关于final成员变量的以下注释src/share/vm/opto/parse1.cpp http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/812ed44725b8/src/share/vm/opto/parse1.cpp#l962:
这个方法(根据Java的规则必须是构造函数)写了一个final。在构造函数发布对新构造函数对象的引用之后,所有初始化的效果必须在任何代码之前提交到内存。我们不等待发布,而是简单地阻止此处的写入。我们不是只对那些需要完成的写入设置障碍,而是强制所有写入完成。
如果存在最终成员变量,编译器会发出附加指令。这些附加指令很可能会导致性能损失。但尚不清楚这种影响是否对任何应用程序都很重要。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)