我一直很乐于深入研究多线程编程的细节,尽管阅读了多年的CPU内存一致性模型,无等待和无锁算法,Java内存模型,实践中的Java并发性等知识,但我始终很喜欢。等等-我仍然会创建多线程编程错误。 总是令人惊奇的谦卑经历,使我想起这个问题有多复杂。

如果您已经阅读过JMM,那么您可能会记得,他们加强的领域之一是保证构造函数完成后最终字段的可见性。 例如,

public class ClassA {public final String b;public ClassA(String b) {this.b = b;}
}
...
ClassA x = new ClassA("hello");

JMM指出,每个线程(甚至构成ClassA实例x的线程除外)都将
始终将 xb视为“ hello”,并且永远不会看到null值(参考字段的默认值)。

真是太好了! 这意味着我们可以通过将字段标记为final来创建不可变对象,并且任何构造的实例都可以自动在线程之间共享,而无需进行其他工作来保证内存可见性。 ! 不利的一面是,如果将ClassA.b标记为final,那么您将没有此类保证。 其他线程可能会观察到xb ==空结果(如果未使用其他“安全发布”机制来获得可见性)

当他们创建新的JMM时,每个人最喜欢的JCP成员Doug Lea都创建了一个食谱,以帮助JVM开发人员实现新的内存模型规则。 如果阅读此内容,那么您将看到“规则”状态,即JIT编译器应在构造函数返回之前立即发出StoreStore内存屏障。 该StoreStore障碍是一种“内存围栏”。 如果在汇编指令中发出该信息,则意味着对栅栏进行重新排序之前, 栅栏之前出现的内存写之前,不能对内存进行任何写(存储)操作。 请注意,它没有说明读取内容,它们可以沿任一方向“跳”栅栏。

那么这是什么意思? 如果您考虑在调用构造函数时编译器的功能,那么很好:

String x = new ClassA("hello");get's broken down in to pseudo-code steps of:1. pointer_to_A = allocate memory for ClassA (mark word, class object pointer, one reference field for String b)
2. pointer_to_A.whatever class meta data = ...
3. pointer_to_A.b = address of "hello" string
4. emit a StoreStore memory barrier per the JMM
5. x = pointer_to_A

步骤4的StoreStore屏障可确保任何写入(例如类元数据和对字段b的写入)都不会在步骤5中对x进行重新排序。这可以确保x是否对任何其他线程可见-如果没有StoreStore内存屏障,那么可以重新排序步骤3和5 并且在写入xb和另一个cpu之前可能会出现对x的主内存写入操作内核可以观察到pointer_to_A.b为0(空),这将违反JMM。

好消息! 但是,如果您看一下该菜谱,就会发现一些有趣的事情:(1)很多人正在许多处理器体系结构上编写JVM! (2)x86上的所有*存储屏障都没有操作,除了StoreLoad屏障! 这意味着在x86上,上面的此StoreStore内存屏障为空操作,因此不会为此发出任何程序集。 它什么都不做! 这是因为x86的内存模型是强大的 “总存储排序”(TSO)。 X86确保观察到所有内存写入,就像它们都是以相同顺序进行的一样。 因此,由于TSO的缘故,写入5永远不会出现在任何其他线程的3之前,并且不需要发出存储屏障。 其他cpu体系结构的内存模型较弱,无法保证这种效果,因此需要StoreStore内存围墙。 请注意,较弱的内存模型虽然可能更难编程或较不直观,但通常要快得多,因为cpu可以对事物进行重新排序以更有效地使用缓存写入并减少缓存一致性工作。

显然,您应该继续遵循JMM编写正确的代码。 但是,这也意味着(不幸或幸运的是)如果您在x86上运行,忘记此操作不会导致错误……就像我在工作中所做的那样。

为了真正钻研这个家并确保没有食谱中可能没有描述的其他副作用,我按此处所述运行了x86程序集输出程序,并捕获了为ClassA调用构造函数的输出(最后一个在引用类型字段)和ClassB的构造函数,该类与ClassA相同,除了在类成员上没有final关键字之外。 x86程序集的输出是相同的 。 因此,从JIT角度来看,在x86(非钛合金,非arm等)上,final关键字没有任何影响。

如果您想知道汇编代码的外观,则如下所示。 请注意,没有任何锁定说明。 当Oracle的7u25 JRE发出x86 StoreLoad内存隔离栅时,它是通过发出锁addl $ 0x0,(%rsp)来完成的 ,它仅向堆栈指针添加零(无操作, 由于其被锁定),因此具有完整的作用。栅栏(符合StoreLoad栅栏的条件)。 x86中有几种导致完全隔离的效果的方法,这些方法在OpenJDK邮件列表中进行了讨论。 他们观察到至少在nehelem intel上,锁添加0是最紧凑/有效的空间。

0x00007f152c020c60: mov    %eax,-0x14000(%rsp)0x00007f152c020c67: push   %rbp0x00007f152c020c68: sub    $0x20,%rsp         ;*synchronization entry; - com.argodata.match.profiling.FinalConstructorMain::callA@-1 (line 60)0x00007f152c020c6c: mov    %rdx,(%rsp)0x00007f152c020c70: mov    %esi,%ebp0x00007f152c020c72: mov    0x60(%r15),%rax0x00007f152c020c76: mov    %rax,%r100x00007f152c020c79: add    $0x18,%r100x00007f152c020c7d: cmp    0x70(%r15),%r100x00007f152c020c81: jae    0x00007f152c020cd60x00007f152c020c83: mov    %r10,0x60(%r15)0x00007f152c020c87: prefetchnta 0xc0(%r10)0x00007f152c020c8f: mov    $0x8356f3d0,%r11d  ;   {oop('com/argodata/match/profiling/FinalConstructorMain$ClassA')}0x00007f152c020c95: mov    0xb0(%r11),%r100x00007f152c020c9c: mov    %r10,(%rax)0x00007f152c020c9f: movl   $0x8356f3d0,0x8(%rax)  ;   {oop('com/argodata/match/profiling/FinalConstructorMain$ClassA')}0x00007f152c020ca6: mov    %r12d,0x14(%rax)   ;*new  ; - com.argodata.match.profiling.FinalConstructorMain::callA@0 (line 60)0x00007f152c020caa: mov    %ebp,0xc(%rax)     ;*putfield a; - com.argodata.match.profiling.FinalConstructorMain$ClassA::@6 (line 17); - com.argodata.match.profiling.FinalConstructorMain::callA@6 (line 60)0x00007f152c020cad: mov    (%rsp),%r100x00007f152c020cb1: mov    %r10d,0x10(%rax)   ;*new  ; - com.argodata.match.profiling.FinalConstructorMain::callA@0 (line 60)0x00007f152c020cb5: mov    %rax,%r100x00007f152c020cb8: shr    $0x9,%r100x00007f152c020cbc: mov    $0x7f152b765000,%r110x00007f152c020cc6: mov    %r12b,(%r11,%r10,1)  ;*synchronization entry; - com.argodata.match.profiling.FinalConstructorMain::callA@-1 (line 60)0x00007f152c020cca: add    $0x20,%rsp0x00007f152c020cce: pop    %rbp0x00007f152c020ccf: test   %eax,0x9fb932b(%rip)        # 0x00007f1535fda000;   {poll_return}0x00007f152c020cd5: retq   0x00007f152c020cd6: mov    $0x8356f3d0,%rsi   ;   {oop('com/argodata/match/profiling/FinalConstructorMain$ClassA')}0x00007f152c020ce0: xchg   %ax,%ax0x00007f152c020ce3: callq  0x00007f152bfc51e0  ; OopMap{[0]=Oop off=136};*new  ; - com.argodata.match.profiling.FinalConstructorMain::callA@0 (line 60);   {runtime_call}0x00007f152c020ce8: jmp    0x00007f152c020caa  ;*new; - com.argodata.match.profiling.FinalConstructorMain::callA@0 (line 60)0x00007f152c020cea: mov    %rax,%rsi0x00007f152c020ced: add    $0x20,%rsp0x00007f152c020cf1: pop    %rbp0x00007f152c020cf2: jmpq   0x00007f152bfc8920  ;   {runtime_call}
参考: x86上的Java final字段是否没有操作? 来自我们的JCG合作伙伴史蒂夫·阿什(Steve Ash),来自“多杯咖啡”博客。

翻译自: https://www.javacodegeeks.com/2013/11/java-final-fields-on-x86-a-no-op.html

x86上的Java最终字段没有操作?相关推荐

  1. java 反射操作字段_x86上的Java最终字段没有操作?

    java 反射操作字段 我一直很乐于深入研究多线程编程的细节,并且尽管阅读了多年的CPU内存一致性模型,无等待和无锁算法,Java内存模型,实践中的Java并发性等知识,但我总是很喜欢.等等-我仍然会 ...

  2. mac java 创建文件夹_在mac电脑上创建java的一些简单操作

    首先你要在电脑上下载一个JDK创造出一个java环境 如下第二个: 然后步奏如下: step1: 苹果->系统偏好设置->最下边点MySQL 在弹出页面中 关闭mysql服务(点击stop ...

  3. 使用Java反射更改私有静态最终字段

    我有一个带有private static final字段的类,不幸的是,我需要在运行时进行更改. 使用反射我得到此错误: java.lang.IllegalAccessException: Can n ...

  4. java反射 面试题_使用Java反射更改私有静态最终字段

    假设没有SecurityManager阻止你执行此操作,则可以使用setAccessible来绕开private并重置修饰符以摆脱final,并实际上修改private static final字段. ...

  5. 使用Java 8 Stream像操作SQL一样处理数据(上)

    转载自 使用Java 8 Stream像操作SQL一样处理数据(上) 几乎每个Java应用都要创建和处理集合.集合对于很多编程任务来说是一个很基本的需求.举个例子,在银行交易系统中你需要创建一个集合来 ...

  6. github上创建java项目简单操作

    github上创建java项目简单操作 参考L: github上创建java项目简单操作 - CSDN博客 http://blog.csdn.net/qq_29392425/article/detai ...

  7. linux下float的寄存器,检测x86上Linux的非正常浮动操作(Detecting denormal float operations on Linux for x86)...

    检测x86上Linux的非正常浮动操作(Detecting denormal float operations on Linux for x86) 我正在将一个windows程序移植到linux的过程 ...

  8. JAVA中用 SQL语句操作小结

    1.添加记录(INSERT) 使用SQL语句的INSERT命令可以向数据库中插入记录,INSERT命令的基本形式为: INSERT INTO 表名 [(字段名1,字段名2-)] VALUES (值1, ...

  9. 使对易失性字段的操作原子化

    总览 易失字段的预期行为是,它们在多线程应用程序中的行为应与在单线程应用程序中的行为相同. 禁止它们表现相同的方式,但不能保证它们表现相同的方式. Java 5.0+中的解决方案是使用AtomicXx ...

最新文章

  1. linux测试nvme性能,使用Python测试NVMe吞吐量
  2. pytorch nan解决方法笔记
  3. renderthread是什么_Android 旗舰机标配的高帧屏(120Hz),对各位 App 开发者有什么影响?...
  4. 机器学习帮助WebRTC视频质量评价
  5. 哪有那么多「能力问题」,在你牛到一定程度前,全是「态度问题」
  6. java 图像处理两例:图像缩放与圆角图片的制作
  7. python(源码包安装 基本使用 循环)
  8. 阿里云服务器使用xshell连接
  9. jquery File upload 的一个例子
  10. Mathmatica9 注册不了
  11. 无线蓝牙通信的c语言,蓝牙与C和winsock
  12. mysql flush logs时出现ERROR 1105
  13. win7系统服务print spooler 无法启动解决方法(开启及关闭方法)
  14. 我使用Feign上传文件踩的坑,MultipartFile文件死活传不过去
  15. 用计算机绘制采油曲线,绘制采油曲线.ppt
  16. [RK3288][Android6.0] 调试笔记 --- ECHI上的USB Camera无法打开
  17. Python对带光晕的任意纯色背景图像进行去背景色操作(保留透明效果、附源码)
  18. 字符串匹配:Sunday算法
  19. 戴尔中国“三包服务”相关问答
  20. 数字图像处理的招聘公司

热门文章

  1. ReviewForJob——深度优先搜索的应用
  2. 一文理类加载相关知识:类加载器、双亲委派、SPI
  3. vue框架项目部署到服务器_在浏览器中在线尝试无服务器框架项目!
  4. java全局变量和局部变量_Java 10 –局部变量类型推断
  5. angluar cdk_零分钟即可在容器开发套件(CDK)上实现云运营
  6. jmx 替代_使用JMX作为Ganglia的现代替代品进行CLDB监视
  7. jvm gc阻塞时长 占比_jvm进行转义分析需要多长时间? 可能比您想象的要长。
  8. spark wai_WAI-ARIA对自动完成小部件的支持
  9. Java XMPP负载测试工具
  10. 制作程序化装饰花纹图案_装饰图案