JVM、Java Memory Model 与 CPU:为何在 x86 上正常工作,却在 ARM 上出错

发布: (2025年12月15日 GMT+8 10:17)
5 min read
原文: Dev.to

Source: Dev.to

引言

“但在我的机器上从未出现过问题。”
很可能是因为你的机器是 x86。换成 ARM,一些“沉睡”的并发 bug 就会出现。

核心思想:x86 在实践中往往更“保守”,而 ARM 允许更多的重排。如果你的代码依赖硬件的“好心”行为,它可能在 x86 上能跑通,却在 ARM 上失败。

Java 内存模型 (JMM)

JMM 并不仅仅是“线程共享内存,就这样”。它定义了:

  • 可见性 – 一个线程写入的内容何时能被另一个线程看到。
  • 顺序 – 允许哪些重排。
  • happens‑before – 在线程之间建立真实保证的关系。

如果两个操作之间没有 happens‑before,则没有以下保证:

  • 看到最新的值;
  • 按写入的顺序看到数据;
  • 看到一个“已经准备好”的对象。

这就是许多“幽灵” bug 的根源。

经典示例

x = 42;
ready = true;

很多人以为,只要看到 ready == true,另一线程就一定会看到 x == 42。JMM 允许在没有同步的情况下,另一线程观察到:

ready == true
x == 0

为什么?

  • x 可能仍在缓存/寄存器中;
  • 写操作可能被重排;
  • 另一线程可能读取到旧值。

这是一种极其隐蔽的 bug。

对象的安全发布

instance = new MyObject();

虽然看起来是一次操作,但底层实际上会经历:

  1. 分配内存;
  2. 初始化字段(构造函数);
  3. 发布引用(instance 指向对象)。

如果没有同步,JVM/CPU 实际上可能允许发布步骤(3)在初始化步骤(2)对其他线程可见之前就发生。于是第二个线程可能执行:

if (instance != null) {
    instance.doSomething();
}

instance 已经不是 null,但对象的字段仍可能处于“默认”状态(0/null)。在 double‑checked locking 等模式中,这已经导致了真实的生产环境问题。

使用 volatile

private static volatile MyObject instance;

将引用声明为 volatile 带来两个重要保证:

  1. 可见性 – 读取 volatile 时会看到最新的值(不会被本地缓存卡住)。
  2. 顺序volatile 在变量周围创建屏障,阻止某些重排。

volatile 的写 happens‑before 随后对同一 volatile 的任何读取。实际上,如果线程 B 读取到非空的 instance(volatile),它就保证能看到线程 A 在发布引用之前所做的所有写入(包括构造函数中对对象的写入)。

JIT 如何处理 volatile

volatile 的写

在编译对 volatile 的写入时,JVM 必须确保:

  • 之前的所有写入不会被“挂起”而不可见;
  • 该值的发布不会“抢先”于之前的操作。

实现方式是围绕访问插入 memory fences/barriers(具体指令取决于架构和 JIT)。

volatile 的读

在编译对 volatile 的读取时,JVM 必须确保:

  • 不会从缓存/寄存器中读取到旧值;
  • 随后的读取不会被移动到这次读取之前。

同样会使用屏障和具有相应语义的指令。

x86 与 ARM

x86 上,许多重排不那么激进,平台往往会“帮忙”,即使你没有显式请求。这 并不 表明代码是正确的——只是 bug 可能不易显现。

ARM 上,允许的重排更多,架构要求显式同步来保证顺序和可见性。如果没有使用 volatilesynchronized、锁或原子类,ARM 更容易暴露出 bug。

结果:同一个在 x86 上“正常”的程序,在 ARM 上高负载时可能会失败。

实用规则(不谈哲学)

如果存在线程间共享,选择合适的策略:

策略何时使用
volatile简单标志/状态以及安全发布引用
synchronized / Lock不变式和复合操作
Atomic*无锁的原子操作(CAS),但有各自的成本和限制

如果没有显式的 happens‑before,就是在押注架构和运气。

Back to Blog

相关文章

阅读更多 »

Java的历史

Origins Java 于1991年由 James Gosling 发起,作为名为 Green Project 的研究计划的一部分。该项目的主要目标是创建一个……