64 位 OpenJDK 7/8 中并发长写入的值完整性保证

2023-12-31

注意:这个问题与 volatile、AtomicLong 或所描述的用例中任何明显的缺陷无关。

我试图证明或排除的性质如下:

鉴于以下情况:

  • 最近的 64 位 OpenJDK 7/8(最好是 7,但 8 也有帮助)
  • 基于 Intel 的多处理系统
  • 非易失性长原始变量
  • 多个不同步的变异线程
  • 不同步的观察者线程

观察者是否总是能保证遇到由修改器线程写入的完整值,或者单词撕裂是否存在危险?

JLS:尚无定论

此属性存在于 32 位基元和 64 位对象引用中,但 JLS 不保证长整型和双精度:

17.7。双精度和长精度的非原子处理: http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7
出于 Java 编程语言内存模型的目的,对非易失性 long 或 double 值的单次写入被视为两次单独的写入:每个 32 位一半写入一次。这可能会导致线程从一次写入中看到 64 位值的前 32 位,而从另一次写入中看到第二个 32 位。

但请稳住你的马:

[...]为了提高效率,这种行为是特定于实现的; Java 虚拟机的实现可以自由地以原子方式或分两部分执行对 long 和 double 值的写入。鼓励 Java 虚拟机的实现尽可能避免拆分 64 位值。 [...]

所以,JLSallows用于分割 64 位写入的 JVM 实现,以及鼓励开发商也相应调整鼓励JVM 实现者坚持使用 64 位写入。我们还没有最新版本的 HotSpot 的答案。

HotSpot JIT:谨慎乐观

由于单词撕裂最有可能发生在紧密循环和其他热点的范围内,因此我尝试分析 JIT 编译的实际程序集输出。长话短说:需要进一步测试,但我只能看到长整型上的原子 64 位操作。

I used hdis https://github.com/drazzib/openjdk-hsdis,OpenJDK 的反汇编器插件。 在我老化的 OpenJDK 7u25 版本中构建并安装了该插件后,我开始编写一个简短的程序:

public class Counter {
  static long counter = 0;
  public static void main(String[] _) {
    for (long i = (long)1e12; i < (long)1e12 + 1e5; i++)
      put(i);
    System.out.println(counter);
  }

  static void put(long v) {
    counter += v;
  }
}

我确保始终使用大于 MAX_INT 的值(1e12 到 1e12+1e5),并重复操作足够多次(1e5)以触发 JIT。

编译后,我使用 hdis 执行了 Counter.main(),如下所示:

java -XX:+UnlockDiagnosticVMOptions \ 
     -XX:PrintAssemblyOptions=intel \
     -XX:CompileCommand=print,Counter.put \ 
     Counter

JIT 为 Counter.put() 生成的程序集如下(为方便起见添加了十进制行号):

01   # {method} 'put' '(J)V' in 'Counter'
02 ⇒ # parm0:    rsi:rsi   = long
03   #           [sp+0x20]  (sp of caller)
04   0x00007fdf61061800: sub    rsp,0x18
05   0x00007fdf61061807: mov    QWORD PTR [rsp+0x10],rbp  ;*synchronization entry
06                                                 ; - Counter::put@-1 (line 15)
07   0x00007fdf6106180c: movabs r10,0x7d6655660    ;   {oop(a 'java/lang/Class' = 'Counter')}
08 ⇒ 0x00007fdf61061816: add    QWORD PTR [r10+0x70],rsi  ;*putstatic counter
09                                                 ; - Counter::put@5 (line 15)
10   0x00007fdf6106181a: add    rsp,0x10
11   0x00007fdf6106181e: pop    rbp
12   0x00007fdf6106181f: test   DWORD PTR [rip+0xbc297db],eax        # 0x00007fdf6cc8b000
13                                                 ;   {poll_return}

有趣的行标有“⇒”。 如您所见,加法运算是使用 64 位寄存器在四字(64 位)上执行的(rsi http://en.wikipedia.org/wiki/RSI_register#64-bit).

我还尝试通过在“长计数器”之前添加字节类型的填充变量来查看字节对齐是否是一个问题。汇编输出的唯一区别是:

before

    0x00007fdf6106180c: movabs r10,0x7d6655660    ;   {oop(a 'java/lang/Class' = 'Counter')}

after

    0x00007fdf6106180c: movabs r10,0x7d6655668    ;   {oop(a 'java/lang/Class' = 'Counter')}

两个地址都是 64 位对齐的,并且那些“movabs r10, ...”调用正在使用 64 位寄存器。

到目前为止,我只测试了加法。我认为减法的行为类似。
其他操作,例如按位运算、赋值、乘法等仍有待测试(或由足够熟悉 HotSpot 内部结构的人确认)。

译者:尚无定论

这给我们留下了非 JIT 场景。我们来反编译Compiler.class:

$ javap -c Counter
[...]
static void put(long);
Code:
   0: getstatic     #8                  // Field counter:J
   3: lload_0
   4: ladd
   5: putstatic     #8                  // Field counter:J
   8: return
[...]

...我们将对第 7 行的“ladd”字节码指令感兴趣。 然而,我一直无法到目前为止特定于平台的实现。

感谢您的帮助!


事实上,你已经回答了你自己的问题。

不存在“非原子处理”double and long在 64 位 HotSpot JVM 上, 因为

  1. HotSpot 使用 64 位寄存器来存储 64 位值(x86_64.ad http://hg.openjdk.java.net/jdk8u/hs-dev/hotspot/file/5a061b65b00b/src/cpu/x86/vm/x86_64.ad#l263 vs. x86_32.ad http://hg.openjdk.java.net/jdk8u/hs-dev/hotspot/file/5a061b65b00b/src/cpu/x86/vm/x86_32.ad#l163).
  2. HotSpot 在 64 位边界上对齐 64 位字段(宇宙.inline.hpp http://hg.openjdk.java.net/jdk8u/hs-dev/hotspot/file/5a061b65b00b/src/share/vm/memory/universe.inline.hpp#l38)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

64 位 OpenJDK 7/8 中并发长写入的值完整性保证 的相关文章

  • Java中如何动态添加charsequence[]中的数据?

    初始化的一种方法charsequence is charsequence item abc def 但我不想以这种方式初始化它 有人可以建议其他方式吗 比如我们初始化的方式string arrays 首先 修复变量声明 charsequen
  • Java泛型 - 实现像map这样的高阶函数

    我决定用 Java 编写一些常见的高阶函数 map filter reduce 等 这些函数通过泛型实现类型安全 但我在一个特定函数中遇到通配符匹配问题 为了完整起见 函子接口是这样的 The interface containing th
  • 在java中将RFC3339 DateTime转换为Date [重复]

    这个问题在这里已经有答案了 如何转换RFC 3339 https www rfc editor org rfc rfc3339java 中的 com google api client util DateTime 到 DateTime 例如
  • java本地时间格式不带年份

    我喜欢将本地时间格式格式化为不带年份的字符串 目前我可以显示包含年份的本地格式 java text DateFormat df java text DateFormat getDateInstance java text DateForma
  • 使用 Intellij 2017.2 /out 目录构建会重复 /build 目录中的文件

    更新到 Intellij 2017 2 后 构建我的项目会创建一个 out包含生成的源文件和资源文件的目录 这些文件与已包含的文件重复 build并导致duplicate class生成的类的编译器错误 关于 Gradle 或 Intell
  • 如何在谷歌地图中使用latlng字符串数组绘制多边形

    在我的应用程序中 我有包含 imagview 的 recyclerview 并且该 imageview 通过使用我存储在 sqlite 中的坐标包含静态地图图像 当我单击该图像时 我将该字符串数组格式的坐标传递给其他地图活动 然后使用该字符
  • Junit Mockito 测试一切

    我现在正在寻找更多时间但没有结果 请帮忙 这是我要测试的课程 public class DBSelectSchema extends Database private static final Logger LOG Logger getLo
  • 为什么 hibernate 在一张表中保存两个 @OneToMany 列表?

    想象一下使用 Hibernate 和 JPA 的简化代码如下 Entity class C Id GeneratedValue public long id MappedSuperclass abstract class A Id Gene
  • 使用 google-api-java-client 的 2 足 OAuth

    有谁知道如何将 2 legged OAuth 与 google api java client 一起使用 我正在尝试访问 Google Apps 配置 API 以获取特定域的用户列表 以下不起作用 HttpTransport transpo
  • 具有最小刻度的图表的漂亮标签算法

    我需要手动计算图表的刻度标签和刻度范围 我知道漂亮刻度的 标准 算法 参见 我也知道这个Java实现 http erison blogspot nl 2011 07 algorithm for optimal scaling on char
  • 向 JList 添加滚动条? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何将 JList 添加到 JScrollPane 把你的JList in a JScrollPane JScrollPane scrol
  • 如何使用键盘上的“删除”按钮作为从 JTable 中删除行的快捷方式[重复]

    这个问题在这里已经有答案了 可能的重复 如何制作删除按钮来删除JTable中的行 https stackoverflow com questions 13236206 how to make delete button to delete
  • 将字符串转换为字符并按降序排序(ascii)

    我正在创建一个程序 该程序将使用户输入整数 一个接一个 存储在数组中并按降序显示整数 该程序还要求用户输入一个字符串 使用以下命令将其转换为字符string toCharArray 我已经正确地按降序显示整数 问题是我不知道如何按降序显示字
  • Visual Studio Code - Java 类路径不完整。只会报告语法错误

    在使用 python 获得了丰富的经验之后 我正在使用 java 迈出第一步 我正在运行的脚本是一个简单的 Java Swing Gui 它可以从命令行和 VS Code 中正常编译和运行 为了设置 java 调试环境 我使用 github
  • Wildfly 10.1 消耗所有核心

    我们最近将银行应用程序从 java 1 6 升级到 1 8 将 jboss 4 x 升级到 wildfly 10 1 我们观察到 java 消耗了机器上可用的所有核心 10 有人可以告诉是什么原因吗 通常情况下 jboss 4 x 的最大
  • Java XML 解析器添加不必要的 xmlns 和 xml:space 属性

    我在 Windows 10 上使用 Java 11 AdoptOpenJDK 11 0 5 2019 10 15 我正在解析一些旧版 XHTML 1 1 文件 这些文件采用以下一般形式
  • Eclipse 在单独的窗口中打开代码

    我正在 eclipse 中编程 在两个显示器设置上运行 在其中一台显示器上 我只获得了项目资源管理器和编辑器作为自定义透视图 而在另一台显示器上 我获得了其他工具 例如控制台 调试 任务 变量 断点等 例如 当我单击任务视图中的任务时 这将
  • Jackson 的 ObjectMapper 和 SQL 中的 RowMapper

    我们正在使用对象映射器 当将 ObjectMapper 与 RowMapper 一起使用时 是否应该在每个 mapRow 内部 如下所示 声明它 还是在 mapRow 外部声明为类公共成员 我认为根据本文 它应该作为公共类成员在外部 我应该
  • 删除Java中重载的方法

    有2个重载方法 这些方法中的每一个都将一种类型的列表转换为不同类型的列表 但第一种方法使用比较器 class SomeClass public static
  • 用 lambda 表达式替换匿名函数

    我在 Java 8 映射操作中传递一个函数 Intellij 告诉我它可以用 lambda 表达式替换 但我不知道如何在不创建中间对象结构的情况下做到这一点 这就是我所做的 List

随机推荐