我们知道,将字段设为final通常是一个好主意,因为我们获得了线程安全性和不变性,这使得代码更容易推理。我很好奇是否有相关的性能成本。
Java Memory Model保证了这个final Field Semantics
一个线程只能在对象完全初始化后才能看到对该对象的引用,它可以保证看到该对象的最终字段的正确初始化值。
这意味着对于像这样的类
class X {
X(int a) {
this.a = a;
}
final int a;
static X instance;
}
每当Thread 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
),Thread2可以看到默认初始化值并打印0。
当JIT看到final
时,它显然会克制住不重新排序。但它也必须迫使CPU服从命令。是否存在相关的性能损失?
发布于 2014-05-08 04:07:48
是否存在相关的性能损失?
如果您查看一下JIT编译器的源代码,您将在文件src/share/vm/opto/parse1.cpp中找到关于最终成员变量的以下注释
这个方法(根据Java的规则,它必须是一个构造函数)写了一个final。在构造函数发布对新构造函数对象的引用之后,必须在任何代码之前将所有初始化的效果提交到内存中。而不是等待发布,我们只需在此处阻止写入。我们不是只对那些需要完成的写操作设置障碍,而是强制所有写操作都完成。
如果有最终成员变量,编译器会发出额外的指令。最有可能的是,这些额外的指令会导致性能损失。但目前还不清楚,这种影响是否对任何应用程序都有重大影响。
https://stackoverflow.com/questions/23527252
复制相似问题