当前位置: 首页 > 知识库问答 >
问题:

假设我知道将在x64 CPU上运行,那么我可以忽略哪些JVM同步实践?

齐向笛
2023-03-14

我知道JVM内存模型是为cpu的最小公分母而设计的,因此它必须假设JVM可以运行的cpu的最脆弱的模型(例如ARM)。

现在,考虑到x64具有相当强大的内存模型,假设我知道我的程序只能在64位x86 CPU上运行,我可以忽略哪些同步实践?当我的程序通过虚拟化运行时,这也适用吗?

示例:
众所周知,JVM的内存模型需要同步对long和double的读/写访问,但可以假设其他32位原语(如int、float等)的读/写是原子的。

但是,如果我知道我在64位x86机器上运行,我是否可以忽略在long s/double上使用锁,因为我知道cpu会自动读取/写入64位值并保持它们不稳定(就像我使用int/Floats一样)?

共有3个答案

慕俊语
2023-03-14

始终包括JVM内存模型声明需要的内存障碍,然后让JVM针对不同的平台进行优化。

知道您只在x86 CPU上运行并不意味着您可以放弃使用内存屏障。除非您知道您将只在单核x86 CPU上运行;)在当今的多核世界中,没有人真正知道这一点。

为什么?因为java内存模型有两个主要问题。

  1. 内核和内核之间数据的可见性
  2. 发生在保证之前,也就是重新排序。

如果没有内存屏障,对其他内核可见的操作顺序可能会变得非常混乱;即使x86提供了更有力的保证,情况也是如此。x86仅在数据进入cpu缓存后才能确保一致性,虽然其排序保证非常强,但只有在Hotspot通知cpu将数据写入缓存后,它们才会生效。

如果没有易失性/同步,那么编译器(javac和热点)将决定他们何时进行这些写入以及以什么顺序进行。他们决定在寄存器中长时间保留数据是完全有效的。一旦跨越易失性或同步内存障碍,JVM就知道告诉CPU将数据发送到缓存。

正如Doug Lea在JSR-133 Cookbook中记录的那样,大多数x86障碍都被简化为保证排序的无操作指令。因此,JVM将使指令对我们来说尽可能高效。编写Java内存模型的代码,让Hotspot发挥其魔力。如果Hotspot可以证明不需要同步,它可以完全放弃它。

最后,双重检查锁定模式在多核x86上也被证明是错误的;尽管它有更强的内存保证。Bartos Milewski在他的C博客上写了一些很好的细节,这次也是专门针对Java的

计燕七
2023-03-14

您仍然需要处理线程安全,因此波动性语义和内存Geofence仍然很重要

我这里的意思是,例如在Oracle Java中,大多数低级同步操作最终都是不安全的(docjar.com/docs/api/sun/misc/Unsafe.html#getUnsafe),而这又有一长串本机方法。因此,最终,这些同步实践和许多其他低级操作都被所在的JVM封装起来。x64与x86的jvm不同。

再次阅读您编辑的问题后:加载/存储操作的原子性是这里的一个主题。所以不,您不必担心x64上的原子64位加载/存储。但由于这不是所有同步问题的终结,请参阅其他答案。

东方俊明
2023-03-14

我知道JVM内存模型是为cpu的最小公分母而设计的,因此它必须假设JVM可以运行的cpu的最脆弱的模型(例如ARM)。

这是不正确的。JMM是多种竞争力量妥协的结果:对较弱内存模型的渴望,以便程序可以在具有较弱内存模型的硬件上运行得更快;编译器编写者希望允许某些优化的愿望;以及并行Java程序的结果是正确和可预测的,并且如果可能的话(!)Java程序员可以理解的愿望。有关内存模型问题的一般概述,请参阅Sarita Adve的CACM文章。

考虑到x64有一个相当强大的内存模型,假设我知道我的程序将只在[x64]CPU上运行,那么我可以忽略哪些同步实践?

没有一个问题是,内存模型不仅适用于底层硬件,而且还适用于执行程序的JVM,实际上主要是JVM的JIT编译器。编译器可能会决定应用内存模型中允许的某些优化,但如果您的程序对基于底层硬件的内存行为做出了不必要的假设,那么您的程序将崩溃。

您询问了x64和原子64位写入。x64机器上可能永远不会发生字词撕裂。我怀疑任何即时编译器都会将64位值撕裂为32位写入作为优化,但您永远不知道。然而,您似乎不太可能使用此功能来避免程序中的同步或易失性字段。如果没有这些,对这些变量的写入可能永远不会对其他线程可见,或者它们可能会被任意重新排序,可能会导致程序中的错误。

我的建议是首先正确应用同步以使您的程序正确。您可能会感到惊喜。同步操作已经过大量优化,在常见情况下可以非常快。如果您发现存在瓶颈,请考虑使用优化,例如锁拆分、使用挥发物或转换为非阻塞算法。

更新

OP将问题更新为更具体一点,即使用volatile而不是锁和同步。

事实证明,volatile不仅具有内存可见性语义。它还使longdouble访问原子化,这对于这些类型的非易失性变量来说并非如此。请参见JLS第17.7节。您应该能够依靠volatile在任何硬件上提供原子性,而不仅仅是x64。

在我进行这项工作时,有关Java内存模型的更多信息,请参阅AlekseyShipilev的JMM语用学谈话记录。(Aleksey也是JMH的人。)这次演讲有很多细节,还有一些有趣的练习来测试一个人的理解能力。这篇演讲的一个总体要点是,依靠直觉了解内存模型的工作原理(例如缓存线或写缓冲区)通常是错误的。JMM是一种关于内存操作和各种约束(与同步、之前发生等)的形式主义,这些约束决定了这些操作的顺序。这可能会产生违反直觉的结果。试图通过考虑特定的硬件属性来智取JMM是不明智的。它会回来咬你的。

 类似资料:
  • 我得到了“错误o.a.j.e.RegexExtractor:模式错误:等等b;啊等等”作为我的调试错误之一。是否可以将其设置为忽略或至少不在Jeter调试日志中显示任何错误? 谢谢,P

  • 问题内容: 我想在我的JVM上尝试CompressedOops。不,我不知道默认情况下是否可以启用它。我在debian / squeeze上运行此jvm: 有人说它是默认启用的,有人说没有启用: 来自:http : //forums.yourkit.com/viewtopic.php?f=3&t=3185 是的,您是对的,我也选中了它,并且在Java6u21 64位中默认情况下未激活Compres

  • 问题内容: 我可以运行多个JVM吗?如果是,那么我如何找到在哪个JVM上加载了特定的类? 问题答案: 您的意思尚不完全清楚,但是: 您可以在同一台计算机上安装多个VM(版本/品牌等) 您可以运行多个进程,无论是相同的JVM版本还是不同的JVM版本 除非您正在运行调试代理或类似的代理,否则我不知道有什么方法可以询问JVM进程是否加载了特定的类。似乎有点奇怪的要求-为什么要这样做?

  • 什么JVM在CPU+GPU上运行? 我知道像Oracle、Zulu、OpenJDK这样的JVM,但没有一个是为了将处理卸载到GPU上而设计的。

  • 问题内容: 是否有可能使Eclipse忽略错误“未处理的异常类型”? 在我的特定情况下,原因是我已经检查了文件是否存在。因此,我认为没有理由放入try catch语句。 还是我错过了什么? 问题答案: 是否有可能使Eclipse忽略错误“未处理的异常类型FileNotFoundException”。 否。那将是无效的Java,并且Eclipse不允许您更改语言规则。(有时您可以尝试运行无法编译的代