![](https://csdnimg.cn/release/download_crawler_static/86329100/bg3.jpg)
极客时间 | 深入拆解 Java 虚拟机
第一,在一开始便将 a 加载至某一寄存器中,并且在接下来 b 的赋值操作以及使用 b 的代码中避
免使用该寄存器。第二,在真正使用 r2 时才将 a 加载至寄存器中。这么一来,在执行使用 b 的代
码时,我们不再霸占一个通用寄存器,从而减少需要借助栈空间的情况。
int a=0, b=0;
public void method1() {
for (..) {
int r2 = a;
b = 1;
.. // Code uses r2 and rewrites a
}
}
另一个例子则是将第一个方法的代码放入一个循环中。除了原本的两条赋值语句之外,我只在循环
中添加了使用 r2,并且更新 a 的代码。由于对 b 的赋值是循环无关的,即时编译器很有可能将其
移出循环之前,而对 r2 的赋值语句还停留在循环之中。
如果想要复现这两个场景,你可能需要添加大量有意义的局部变量,来给寄存器分配算法施加压
力。
可以看到,即时编译器的优化可能将原本字段访问的执行顺序打乱。在单线程环境下,由于 as-if-
serial 的保证,我们无须担心顺序执行不可能发生的情况,如(r1,r2)=(1,2)。
然而,在多线程情况下,这种数据竞争(data race)的情况是有可能发生的。而且,Java 语言规范
将其归咎于应用程序没有作出恰当的同步操作。
Java 内存模型与 happens-before 关系
为了让应用程序能够免于数据竞争的干扰,Java 5 引入了明确定义的 Java 内存模型。其中最为重
要的一个概念便是 happens-before 关系。happens-before 关系是用来描述两个操作的内存可见性
的。如果操作 X happens-before 操作 Y,那么 X 的结果对于 Y 可见。
在同一个线程中,字节码的先后顺序(program order)也暗含了 happens-before 关系:在程序
控制流路径中靠前的字节码 happens-before 靠后的字节码。然而,这并不意味着前者一定在后者
之前执行。实际上,如果后者没有观测前者的运行结果,即后者没有数据依赖于前者,那么它们可
能会被重排序。
除了线程内的 happens-before 关系之外,Java 内存模型还定义了下述线程间的 happens-before
关系。