使用最终字段的成本

我们知道,使字段最终确定通常是一个好主意,因为我们获得了线程安全性和不可变性,这使得代码更容易推理。我很好奇是否有相关的性能成本。

Java 内存模型保证了最终的字段语义

如果线程只能在对象完全初始化后才能看到对该对象的引用,则保证可以看到该对象的最终字段的正确初始化值。

这意味着对于这样的类

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。如果没有修饰符,JIT 或 CPU 可以对存储区重新排序(先对存储区进行排序,然后再设置),并且 Thread 2 可以看到默认初始化的值并打印 0。finalX.instancea=43

当JIT看到它时,它显然不会重新排序。但它也必须迫使CPU服从命令。是否存在相关的性能损失?final


答案 1

是否存在相关的性能损失?

如果您查看 JIT 编译器的源代码,您会发现以下有关文件 src/share/vm/opto/parse1 中最终成员变量的注释.cpp:

此方法(根据Java的规则必须是构造函数)编写了一个 final。在构造函数发布对新构造函数对象的引用之后,必须在任何代码之前将所有初始化的效果提交到内存中。我们不是等待发布,而是简单地阻止这里的写入。我们不是只对那些需要完成的写入设置障碍,而是强制完成所有写入。

如果存在最终成员变量,编译器将发出其他指令。最有可能的是,这些附加指令会导致性能下降。但目前尚不清楚这种影响对任何应用程序是否重要。


答案 2

推荐