繁体   English   中英

volatile 与最终字段性能

[英]volatile vs final fields performance

什么更适合性能? 假设没有或几乎没有争用

  1. 具有可变字段并一一更新的可变类
  2. 具有最终字段的不可变类,更新周期避免了多字段更新,而是重新创建类一次

波动性在每次写入时都需要内存屏障,我想最终字段也是如此? 意思是对象构造时的单个内存屏障?

更新澄清:

考虑到 java 内存模型和当前的硬件,我觉得这个问题本身很有价值,并且一般可以回答。 如果您必须假设具体情况:

  • 该对象当然是从多个线程访问的,否则这个练习将毫无意义
  • 单个对象是长期存在的,就像在几个小时内一样
  • 这些对象有成百上千个,每秒有成百上千个更新事件

final提示编译器字段值不能更改。 在编译时捕获任何写入尝试。 读取final值不使用内存屏障。 您不能写入final变量,因此内存屏障是没有意义的。

使用提示,编译器(或 JIT)可以用常量替换对最终值的内存引用。 所以在性能方面, final不会引入任何额外的开销。

对于易失性字段,要使易失性字段的更新值在所有访问它的线程之间保持一致,需要在运行时进行协调。 使用volatile引入了必须执行的不变式。

对于不可变的对象,成本是复制对象以反映更改。 存在限制复制量的方法,例如使用持久性数据结构 同样,当您拥有不可变的对象时,您可以进行基于值的比较,并使用诸如备忘录之类的技术来返回已缓存的实例。

因此,每种方法都可能会损害性能,具体取决于您正在执行的操作。

关于不变方法更好的主要事情是它更干净,更容易推理。 对于可变对象,整个身份概念变得晦暗

命令式程序直接操纵其世界(例如内存)。 它建立在一个现在不可持续的单线程前提下-当您查看或更改世界时,世界就停止了。 您说“做到这一点”,它就会发生,“改变这一点”,它就会发生变化。 命令式编程语言主要围绕执行/执行操作以及更改内存位置。

即使在多线程之前,这也不是一个好主意。 添加并发,您就会遇到一个真正的问题,因为“世界已停止”的前提已经不再成立,并且恢复幻觉非常困难且容易出错。 多个参与者(每个参与者的行为似乎无所不能)必须以某种方式避免破坏其他参与者的假设和影响。 这需要互斥锁和锁,以封锁每个参与者要操纵的区域,并需要大量开销才能将更改传播到共享内存,以便其他内核可以看到它们。 效果不是很好。

可变状态方法使您的代码容易受到竞争条件和死锁的影响。 减少可变状态的数量可以减少您遭受这些错误的可能性。

如果垃圾收集器在上次访问旧对象和为新对象提供可用空间之间刷新每个线程的缓存,并且如果没有缓存行包含来自多个对象的数据,那么在大多数平台上自然不可能一个新构造的对象在对该对象的引用存储在该线程可访问的位置之前加载到任何线程的缓存中,即使没有任何读取障碍(超出前面提到的每 GC 循环一次系统) -宽障碍)。 此外,如果编译器可以判断一个对象的多个字段将发生写入,而不会对任何其他可能已公开其引用的对象进行任何干预写入,则它可以省略除最后一个之外的所有对象的写入屏障。

唯一一次使用final字段会比volatile更昂贵,如果它需要创建更多对象来处理可以使用volatile字段“就地”完成的更改。 由于许多因素会影响对象创建成本,因此在特定系统的特定环境下判断哪种方法更有效的唯一可靠方法通常是对两者进行基准测试。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM