x86上的Java最终字段没有操作?

我一直很乐于深入研究多线程编程的细节,尽管阅读了多年的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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366749.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里90后工程师,如何用AI程序写出双11打call歌?

来自阿里巴巴的90后工程师芦阳,用业余时间开发了一套人工智能作词程序——名字叫做MusicGo,这个程序经过芦阳的“喂养”和设定,可以自己写饶舌歌曲。芦阳加入菜鸟也才仅仅四个月,这是他第一次参加双11备战。作为一名新员工&#x…

c语言常用英语单词大全下载,C语言实现英文单词助手

英文单词小助手的实现步骤,供大家参考,具体内容如下题目需求分析:英文单词小助手是帮助学生背诵单词的软件,用户可以选择背诵的词库,并可以编辑自己的词库,还应有词语预览学习功能。系统可以给出中文&#…

通过Jedis API使用排序集

在上一篇文章中,我们开始研究Jedis API和Java Redis Client。 在本文中,我们将研究Sorted Set(zsets)。 排序集的工作方式类似于集,因为它不允许重复的值。 最大的区别在于,在“排序集”中,每个…

编程历史

编程历史: 编程语言的历史早于真正意义的计算机的出现。19世纪就有"可编程的"织布机和钢琴弹奏装置出现,它们都是领域特定语言(DSL)的样例。 从1951年2014年,人类一共发明了256种编程语言,每一种语言的出现都带有某些新…

python删除字符串中指定字符

最近开始学机器学习,学习分析垃圾邮件,其中有一部分是要求去除一段字符中的标点符号,查了一下,网上的大多很复杂例如这样 import re temp "想做/ 兼_职/学生_/ 的 、加,我Q: 1 5. 8 0. !!?? 8 6 。0. 2。 …

vue结合ueditor富文本编辑器(换肤分离)

需求 (PC端)做一个可以使用图片上传、视频上传、文件上传功能的富文本组件,简单的文本编辑发布功能,采用socket方式传输, 做法 当时看到这个需求,我觉得是不难的,就去github上找富文本编辑器,因为项目比较急,当时我…

linux中samba启动不了,Linux_RHEL5中不用关闭SELinux而成功启动Samba,RHEL5中的samba服务器启动后,能 - phpStudy...

RHEL5中不用关闭SELinux而成功启动SambaRHEL5中的samba服务器启动后,能看到共享目录,但是不能访问共享目录,告知权限不够。此时可以通过如下命令:tail /var/log/message看到如此提示:/home/lyy does not exist or perm…

使用MyBatis框架时发现的一些小bug

在大配置MyBatis.xml中: 不能有空节点属性 ,否则启动服务器后点击登录没有反应。 异常问题: ause: java.sql.SQLException: Value 0000-00-00 00:00:00 can not be represented as java.sql.Timestamp: 解决办法: HTT…

JSF Tomcat配置示例

JavaServer Faces (JSF)是一个Web应用程序框架,旨在简化基于Web的用户界面的开发集成。 它用于开发和构建服务器端用户界面组件,并在Web应用程序中使用它们。 JSF技术基于Model-View-Controller (MVC)架构,并且通过在页面中使用可重用的UI组件…

野指针与内存泄漏那些事

野指针:不是NULL指针,是指向垃圾内存的指针 野指针成因: 1.指针变量没有被初始化:指针变量在创建时同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。 2.指针p被free或者delete,没有被设置为…

c语言入门经典案例,c语言入门经典案例及飞源代码.doc

c语言入门经典案例及飞源代码循环控制输出图案【程序1】题目:输出9*9口诀。1.程序分析:分行与列考虑,共9行9列,i控制行,j控制列。2.程序源代码:#include "stdio.h"main(){ int i,j,result; pri…

JS加密算法简单分析

这次分析百度音乐的评论请求的加密,首先先看包 看到有两个地方1. param,2. sign,基本可以断定sign是用的MD5加密的 那么我们从html页面分析入手,恰巧看到html代码中有写到这么一段 右键点击open in Source panel 熟悉的配方&…

RF新手常见问题总结--(基础篇)

RF新手常见问题总结--(基础篇) 学RF快一年了,经常碰到一些问题,有些同学也经常问到,这里总结一些,期望有人后续再补充,主要是响应群里--雪霁大神的号召,技术共享。废话少说,直接上干货了。1. 经…

Java自动拆箱陷阱。 谨防!

您认为以下代码段会显示什么? Object o true ? new Integer(1) : new Double(2.0); System.out.println(o);是! 它将打印: 1.0什么? 1.0? 但是我已经为我的o变量分配了一个Integer 。 为什么打印1.0? 事实证明&…

golang学习笔记(6)--面向接口编程

一、 duck typing duck typing意思是鸭子类型,我们把具备鸭子的行为等部分特征的一个东西叫做鸭子,这是鸭子类型的解释。其实,在go语言中是采用鸭子类型这种思想来实现接口这种编程方式的,我们把一个类只要实现了某接口的方法&…

c语言self用法,C/C++知识点之Self Numbers C语言 UVA640

本文主要向大家介绍了C/C知识点之Self Numbers C语言 UVA640,通过具体的内容向大家展示,希望对大家学习C/C知识点有所帮助。In 1949 the Indian mathematician D.R. Kaprekar discovered a class ofnumbers called self-numbers. For any positive integ…

JS 的平凡之路--学习人气眼中的效果(上)

最近看了看人气眼的界面,感觉到学习的地方有很多呀。这里先带大家看看人气值跳动的实现。本篇代码基于Vue2.x.x。 一、概要 首先看一下效果图: 要想实现上面的效果,我们分为这几个部分: 判断元素是否在可视区域内;函数…

[Swift]LeetCode86. 分隔链表 | Partition List

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)➤GitHub地址&a…

XmlNode与XmlElement的区别总结

原文链接:http://www.cnblogs.com/oilsun/archive/2012/07/07/2580427.html 今 天在做ASP.NET操作XML文档的过程中,发现了两个类:XmlNode和XmlElement。这两个类的功能极其类似(因为我们一般都是在对 Element节点进行操作&#xf…

HOW TO:构造Java类

在这篇HowTo帖子中,我将展示如何将一个类与另一个类一起定型。 为什么这有用? 当您的项目中发生大量BCI时,让每个开发人员编写BCI代码都是不明智的。 首先,这不会抽象出所使用的BCI库。 鉴于Java不支持多重继承,构造型…