Java不可变类与volatile引用对比及final字段性能开销相关技术问询
问题解答
1. 读线程视角下全final不可变类发布的性能优势
答案是肯定的,全final字段的不可变类发布方案在读侧有明确的性能优势。
volatile引用的读取操作本身需要插入读屏障(不同架构下实现不同,x86架构下为loadload屏障,虽然开销极低但仍然存在),且每次读取volatile变量都要触发该屏障操作,编译器也不能对volatile读做任意重排序优化。- 而
final字段的可见性保证是在构造方法执行结束时一次性插入写屏障完成的,读线程读取final字段的时候不需要任何额外的内存屏障,编译器还可以对final字段的读操作做常量折叠、重排序等优化,甚至可以把final字段的值缓存到寄存器里反复使用,不需要每次都从主存/缓存行重读。
2. final字段的性能成本问题
final字段的性能成本和volatile完全不等效,二者的开销发生的时机、频率差异极大:
final的屏障开销仅发生一次,就是在类实例构造完成、赋值给任意引用之前,插入一个StoreStore屏障,保证所有final字段的写操作都完成后,才会把对象引用暴露出去。这个屏障是一次性的,不管后续这个对象被多少线程读多少次,都不会再产生额外开销。- 而
volatile的屏障开销是双向的:写volatile的时候要插入StoreStore+StoreLoad屏障,读volatile的时候要插入LoadLoad+LoadStore屏障,这些开销是每次读写都会发生的,读的频率越高,二者的开销差距越大。 - 从实际执行层面看,二者也完全不等效:JVM可以把
final字段当成常量处理,做各种激进优化,而volatile字段的读写都是禁止重排序的敏感操作,优化空间极小。
补充说明:只有当你把不可变对象的引用跨线程发布的时候,才需要依赖
final的这个可见性保证,如果对象只在单线程内使用,JVM甚至可以把final对应的写屏障都优化掉,完全没有额外开销。
内容的提问来源于stack exchange,提问作者Turin
相关产品推荐
相关产品推荐

