一、摘要
在《JMM之happens-before详解》这篇文章中,我们知道了happens-before规则中的有一条是volatile规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
那么,Java是如何实现这个规则的呢?本文我们将从volatile的作用,cpu的缓存结构以及volatile实例的汇编等几个方面来详细解读volatile的实现原理。
二、volatile关键字的作用
我们从一个例子来解释volatile的作用:
import java.util.concurrent.TimeUnit;
/**
*
*
* @author Sunny
* @version 1.0
* @taskId:
* @createDate 2019/02/18 10:27
* @see com.sunny.concurrent.volatilekey
*/
public class VolatileFoo {
/**
* init_value的最大值
*/
final static int MAX = 5;
/**
* init_value的初始值
*/
static int init_value = 0;
public static void main(String[] args)
{
int x = 10;
x = x++;
System.out.println();
/**
* 启动一个Reader线程,当发现local_value和init_value不同时,则输出init_value被修改的信息
*/
new Thread(() ->
{
int localValue = init_value;
while (localValue < MAX)
{
if (init_value != localValue)
{
System.out.printf("Current thread is [%s] and the init_value is updated to [%d]\n", Thread.currentThread().getName(),init_value);
localValue = init_value;
}
}
}, "Reader").start();
/**
* 启动一个Updater线程,主要用于对init_value的修改,当localValue >= 5 时,则退出生命周期
*/
new Thread(() ->
{
int localValue = init_value;
while (localValue < MAX)
{
System.out.printf("Current thread is [%s] and the init_value will be changed to [%d]\n", Thread.currentThread().getName(), ++localValue);
init_value = localValue;
try
{
// 短暂休眠,目的是为了使Reader线程能够来得及输出变化内容
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}, "Updater").start();
}
}
输出如下:
Current thread is [Updater] and the init_value will be changed to [1]
Current thread is [Updater] and the init_value will be changed to [2]
Current thread is [Updater] and the init_value will be changed to [3]
Current thread is [Updater] and the init_value will be changed to [4]
Current thread is [Updater] and the init_value will be changed to [5]
上面输出之后,Reader线程进入死循环;也就是说Reader线程没有感知到Updater线程对init_value变量的修改;接下来我们对init_value的定义做一下小小的改动,改成如下方式:
static volatile int init_value = 0;
将输出如下结果:
Current thread is [Updater] and the init_value will be changed to [1]
Current thread is [Reader] and the init_value is updated to [1]
Current thread is [Updater] and the init_value will be changed to [2]
Current thread is [Reader] and the init_value is updated to [2]
Current thread is [Updater] and the init_value will be changed to [3]
Current thread is [Reader] and the init_value is updated to [3]
Current thread is [Updater] and the init_value will be changed to [4]
Current thread is [Reader] and the init_value is updated to [4]
Current thread is [Updater] and the init_value will be changed to [5]
Current thread is [Reader] and the init_value is updated to [5]
Process finished with exit code 0
为什么会出现这样的情况呢?其实都是volatile的作用,后面章节会分析其原理。注意:volatile关键字只能修饰类变量和实例变量,对于方法参数、局部变量以及实例常量,类常量都不能进行修饰,比如上面代码中的MAX就不能使用volatile关键字进行修饰。
三、机器硬件CPU与缓存一致性协议
其实在《【转】伪共享、缓存行填充和CPU缓存详述》 这篇文章中有简单讲述CPU的三级缓存结构。
随着CPU的频率不断地得到提升,但受制于制造工艺以及成本等的限制,计算机的内存反倒在访问速度上没有多大的突破,因此CPU的处理速度和内存的访问速度之间的差距越拉越大,通常这种差距可以达到上千倍,极端情况下甚至上万倍以上。
3.1 CPU Cache模型
由于两边速度的严重不对等,通过传统FSB直连内存的访问方式很明显会导致CPU资源受到大量的限制,降低CPU整体的吞吐量,于是就有了在CPU和主内存之间增加缓存的设计,现在的缓存数量都已经增加到了3级,最靠近CPU的缓存称为L1,然后依次是L2,L3和主内存;如图所示:
由于程序指令和程序数据的行为和热点分布差异很大,因此L1 Cache又被划分成了L1i(i是instruction的首字母)和L1d(d是data的首字母)这两种有个字专门用途的缓存,CPU Cache又是由很多个Cache Line构成的,Cache Line可以认为是CPU Cache中的最小缓存单位,目前主流CPU Cache的Cache Line大小都是64字节,CPU Cache模型如图:
L1、L2、L3级Cache出现是为了解决CPU直接访问内存效率低下问题的,程序在运行的过程中,会将运算所需的数据从主存复制一份到CPU Cache中,这样CPU进行计算时就可以直接对CPU Cache中的数据进行读取和写入,当运算结束后,再将CPU Cache中最新数据刷新到主内存中,CPU通过直接访问Cache的方式替代直接访问主存的方式极大地提高CPU的吞吐能力。
3.2 CPU 缓存一致性问题
虽然缓存的出现极大提高了CPU的吞吐能力,但同时也引入了缓存不一致的问题,比如i++这个操作,在程序运行过程中,首先需要将主内存中的数据复制一份存放到CPU Cache中,那么CPU 寄存器在进行数值计算的时候就直接到Cache中读取和写入,当整个过程运算结束之后再将Cache中的数据刷新到主存当中,具体过程如下。
- 读取主内存的i到CPU Cache中。
- 对i进行加1操作。
- 将结果协会到CPU Cache中。
- 将数据刷新到主内存中。
i++在单线程的情况下不会出现任何问题,但是在多线程情况下就会有问题,每个线程都有自己的工作内存(本地内存,对应CPU中的Cache),变量i会在多个线程的本地内存中都存在一个副本。如果同时有两个线程执行i++操作,假设i的初始值为0,每一个线程都从主内存中获取i的值存入CPU Cache中,然后经过计算再写入主内存中,很有可能i在经过两次自增之后结果还是1,这就是缓存不一问致性问题。
为了解决缓存不一致性题,通常主流的解决方法有如下两种:
- 通过总线加锁的方式。
- 通过缓存一致性协议。
第一种方式常见于早起的CPU当中,而且是一种悲观的实现方式,CPU和其他组件的通信都是通过总线(数据总线,控制总线,地址总线)来进行的,如果采用总线加锁的方式,则会阻塞其他CPU对其他组件的访问,从而使得只有一个CPU(抢到总线锁)能够访问这个变量的内存。这种方式效率低下,所以就有了第二种通过缓存一致性协议的方式来解决不一致的问题。
分析缓存一致性协议之前,我们来理解下缓存行,之前也简单提到了缓存行的概念: - 缓存是分段(line)的,一个段对应一块存储空间,我们称之为缓存行,它是CPU缓存中可分配的最小存储单元,大小32字节、64字节、128字节不等,这与CPU架构有关,通常来说是64字节。当CPU看到一条读取内存指令时,它会把内存地址传递给一级数据缓存,一级数据缓存检查它是否有这个内存地址对应的缓存段,如果没有就把整个缓存段从内存(或更高一级的缓存)中加载进来。注意,这里说的是一次加载整个缓存段,这个也是局部性原理。
缓存一致性协议有多种,但是日常处理的大多数计算机设备都属于“嗅探(snooping)”协议,它的基本思想是:
所有的内存的传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线:缓存本身是独立的,但是内存是共享资源,所有的内存访问都要经过仲裁(同一个指令周期中,只有一个CPU缓存可以读写内存)。
CPU缓存不仅仅在做内存传输的时候才与总线打交道,而是不停在嗅探总线上发生的数据交换,跟踪其他缓存在做什么。所以当一个缓存代表他所属的处理器去读写内存时,其他处理器都会得到通知,它们以此来使自己的缓存保持同步。只要某个处理器一写内存,其他处理器马上知道这块内存在它们的缓存段中已失效。
在缓存一致性协议中最为出名的是Intel的MESI协议,MESI协议保证了每一个缓存中使用的共享变量副本都是一致的,它的大致意思是,当CPU再操作Cache中的数据时,如果发现该变量是一个共享变量,也就是说在其他的CPU Cache中也存在一个副本,那么进行如下操作:
- 读取操作,不做任何处理,只是将Cache中的数据读取到寄存器。
- 写入操作,发出信号通知其他CPU将该变量的Cache line置为无效状态,其他CPU在进行该变量读取的时候不得不到主内存中再次获取。
在MESI协议中,每个缓存行有4个状态,可用2个bit表示,它们分别是:
状态 | 描述 | 监听任务 |
---|---|---|
M(Modified) | 该Cache Line有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中 | 缓存行必须时刻监听所有试图读该缓存行相对就主存的操作,这种操作必须在缓存将该缓存行写回主存并将状态变成S(共享)状态之前被延迟执行。 |
E(Exclusive) | 该Cache line有效,数据和内存中的数据一致,数据只存在于本Cache中。 | 缓存行也必须监听其它缓存读主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成S(共享)状态。 |
S(Shared) | 该Cache line有效,数据和内存中的数据一致,数据存在于很多Cache中。 | 缓存行也必须监听其它缓存使该缓存行无效或者独享该缓存行的请求,并将该缓存行变成无效(Invalid)。 |
I(Invalid) | 该Cache line无效。 | 无 |
这里的I、S和M状态已经有了对应的概念:失效/未载入、干净以及脏的缓存段。所以这里新的知识点只有E状态,代表独占式访问,这个状态解决了"在我们开始修改某块内存之前,我们需要告诉其它处理器"这一问题:只有当缓存行处于E或者M状态时,处理器才能去写它,也就是说只有在这两种状态下,处理器是独占这个缓存行的。当处理器想写某个缓存行时,如果它没有独占权,它必须先发送一条"我要独占权"的请求给总线,这会通知其它处理器把它们拥有的同一缓存段的拷贝失效
(如果有)。只有在获得独占权后,处理器才能开始修改数据----并且此时这个处理器知道,这个缓存行只有一份拷贝,在我自己的缓存里,所以不会有任何冲突。
反之,如果有其它处理器想读取这个缓存行(马上能知道,因为一直在嗅探总线),独占或已修改的缓存行必须先回到"共享"状态。如果是已修改的缓存行,那么还要先把内容回写到内存中。
四、volatile关键字深入解析
4.1 volatile关键字的语义
volatile修饰的实例变量或者类变量具备如下两层语义:
- 保证了不同线程之间对共享变量操作时的可见性,也就是说当一个线程修改volatile修饰的变量,另外一个线程会理解看到最新的值。
- 禁止对指令进行重排序
4.1.1理解volatile保证可见性
关于共享变量在多线程间的可见性,在VolatileFoo例子中已经体现的非常透彻了,Updater线程对init_value变量的每一次更改都会使得Reader线程能够看到(在JMM的happens-before的关于volatile规则定义),其步骤具体如下:
- Reader线程从主内存中获取init_value的值为0,并且将其缓存到本地工作内存中。
- Updater线程将init_value的值在本地工作内存中修饰为1,然后立即刷新至主内存中。
- Reader线程在本地工作内存中的init_value失效(反映到硬件上就是CPU的L1或者L2的Cache Line失效)。
- 由于Reader线程工作内存的init_value失效,因此需要到主内存中重新读取init_value的值。
4.1.2理解volatile保证顺序性
volatile关键字对顺序性的保证就比较霸道一点,直接禁止JVM和处理器对volatile关键字修饰的指令进行重排序,但是对于volatile前后无依赖关系的指令则可以随便怎么排序,比如:
int x = 0;
int y = 1;
volatile int z = 20;
x++;
y--;
在语句volatile int z= 20之前,先执行x的定义还是先执行y的定义,我们并不关心,只要能够百分之百地保证在执行到z=20的时候x=0, y=1,同理,关于x的自增以及y的自减操作都必须在z=20之后才能发生。
再看另外一个例子:
private volatile boolean initialized = false;
private Context context;
public Context load() {
if (!initialized) {
context = loadContext();
initialized = true; // 阻止重排序
}
return context;
}
因为initialized 布尔变量增加了volatile的修饰,那就意味着initialized = true;的时候一定是执行且完成了对loadContext()的方法调用。
4.1.2理解volatile保不保证原子性
我们也先来看一个例子:
/**
*
*
* @author Sunny
* @version 1.0
* @taskId:
* @createDate 2019/02/24 11:08
* @see com.sunny.concurrent.volatilekey
*/
public class VolatileNonAtomic {
/**
* 使用volatile修饰共享资源i
*/
private static volatile int i = 0;
private static final CountDownLatch latch = new CountDownLatch(10);
private static void inc() {
i++;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int x = 0; x < 1000; x++) {
inc();
}
/**
* 使计数器减1
*/
latch.countDown();
}).start();
}
// 等待所有的线程完成工作
latch.await();
System.out.println(i);
}
}
上面的这段代码创建了10个线程,每个线程执行1000次对共享变量i的自增操作,但是最终结果肯定不一定是10000,而且每次运行的结果也是各不相同,下面来分析一下其中的原因。
i++的操作其实也是由三步组成的,具体如下:
- 从主内存中获取i的值,然后缓存至线程工作内存中。
- 在线程工作内存中为i进行加1的操作。
- 将i的最新值写入主内存中。
上面的操作单独的每一个操作都是原子性操作,但是合起来就不是,因为在执行中途很有可能会被其他线程打断,例如如下操作情况。
- 假设此时i的值为100,线程A要对变量i执行自增操作,首先它需要到主内存中读取i的值,可是此时由于CPU时间片调度的关系,执行权切换到了线程B,A线程进入了RUNNABLE状态而不是RUNNING状态。
- 线程B同样需要从主内存中读取i的值,由于线程A没有对i做过任何修改操作,因此此时B获取到的i仍然是100.
- 线程B工作内存中为i执行了加1操作,但是未刷新至主内存中。
- CPU时间片的调度又将执行权给了线程A,A线程直接对工作线程中的100进行加1运行(因为A线程已经从主内存中读取了i的值),由于B线程并未写入i的最新值,因此A线程工作空间中的100不会被失效。
- 线程A将i=101写入主内存之中。
- 线程B将i=101写入到主内存中。
这样两次运算实际上只对i进行了一次数值的修改变化。
4.2 volatile的原理和实现机制
通过下面的例子,我们将从Java生成的汇编指令来解析带volatile关键字和不带的差异以及相关原理,例子如下:
/**
*
*
* @author Sunny
* @version 1.0
* @taskId:
* @createDate 2019/02/19 14:22
* @see com.sunny.concurrent.volatilekey
*/
public class VolatileSingleton {
public static VolatileSingleton instance;
public static VolatileSingleton getInstance(){
if(instance == null){
synchronized(VolatileSingleton.class){
if(instance== null){
instance = new VolatileSingleton();
}
}
}
return instance;
}
/**
* @param args
*/
public static void main(String[] args) {
VolatileSingleton.getInstance();
}
}
首先instance属性不带volatile参数,在执行main方法前,我们增加JVM参数:
-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=dontinline,*VolatileSingleton.getInstance -XX:CompileCommand=compileonly,*VolatileSingleton.getInstance -XX:+PrintAssembly
其中,参数 -Xcomp 是让虚拟机以编译模式执行代码,这样代码可以偷懒,不需要执行足够次数来预热都能触发 JIT 编译。两个 -XX:CompileCommand 意思是让编译器不要内联 getInstance() 并且只编译 getInstance(),-XX:+PrintAssembly 就是输出反汇编内容。如果一切顺利的话,控制台会出现如下输出:
Decoding compiled method 0x0000000002b30c50:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000016f42ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton'
# [sp+0x50] (sp of caller)
0x0000000002b30dc0: mov %eax,-0x6000(%rsp)
0x0000000002b30dc7: push %rbp
0x0000000002b30dc8: sub $0x40,%rsp ;*synchronization entry
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@-1 (line 16)
0x0000000002b30dcc: movabs $0xd6556b28,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30dd6: mov 0x68(%r10),%r11d ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@0 (line 16)
0x0000000002b30dda: test %r11d,%r11d
0x0000000002b30ddd: je 0x0000000002b30dee
0x0000000002b30ddf: mov %r11,%rax ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@37 (line 23)
0x0000000002b30de2: add $0x40,%rsp
0x0000000002b30de6: pop %rbp
0x0000000002b30de7: test %eax,-0x2940ded(%rip) # 0x00000000001f0000
; {poll_return}
0x0000000002b30ded: retq
0x0000000002b30dee: mov (%r10),%rax
0x0000000002b30df1: mov %rax,%r10
0x0000000002b30df4: and $0x7,%r10
0x0000000002b30df8: cmp $0x5,%r10
0x0000000002b30dfc: jne 0x0000000002b30f21
0x0000000002b30e02: mov $0x200003df,%r11d ; {metadata('java/lang/Class')}
0x0000000002b30e08: movabs $0x0,%r10
0x0000000002b30e12: lea (%r10,%r11,8),%r10
0x0000000002b30e16: mov 0xa8(%r10),%r10
0x0000000002b30e1d: mov %r10,%r11
0x0000000002b30e20: or %r15,%r11
0x0000000002b30e23: mov %r11,%r8
0x0000000002b30e26: xor %rax,%r8
0x0000000002b30e29: test $0xffffffffffffff87,%r8
0x0000000002b30e30: jne 0x0000000002b30f99 ;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
0x0000000002b30e36: mov $0x7,%ebp
0x0000000002b30e3b: movabs $0xd6556b28,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30e45: cmp 0x68(%r10),%r12d
0x0000000002b30e49: je 0x0000000002b30e75
0x0000000002b30e4b: movabs $0xd6556b28,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30e55: and (%r10),%rbp
0x0000000002b30e58: cmp $0x5,%rbp
0x0000000002b30e5c: jne 0x0000000002b30fd2 ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@28 (line 21)
0x0000000002b30e62: movabs $0xd6556b28,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30e6c: mov 0x68(%r10),%r11d ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@37 (line 23)
0x0000000002b30e70: jmpq 0x0000000002b30ddf
0x0000000002b30e75: mov 0x60(%r15),%rax
0x0000000002b30e79: mov %rax,%r10
0x0000000002b30e7c: add $0x10,%r10
0x0000000002b30e80: cmp 0x70(%r15),%r10
0x0000000002b30e84: jae 0x0000000002b30eff
0x0000000002b30e86: mov %r10,0x60(%r15)
0x0000000002b30e8a: prefetchw 0xc0(%r10)
0x0000000002b30e92: mov $0x2000c105,%r11d ; {metadata('com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30e98: movabs $0x0,%r10
0x0000000002b30ea2: lea (%r10,%r11,8),%r10
0x0000000002b30ea6: mov 0xa8(%r10),%r10
0x0000000002b30ead: mov %r10,(%rax)
0x0000000002b30eb0: movl $0x2000c105,0x8(%rax) ; {metadata('com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30eb7: mov %r12d,0xc(%rax)
0x0000000002b30ebb: mov %rax,%r10
0x0000000002b30ebe: mov %r10,0x20(%rsp) ;*new ; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@17 (line 19)
0x0000000002b30ec3: mov %r10,%rdx
0x0000000002b30ec6: nop
0x0000000002b30ec7: callq 0x0000000002a661a0 ; OopMap{[32]=Oop off=268}
;*invokespecial
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@21 (line 19)
; {optimized virtual_call}
0x0000000002b30ecc: movabs $0xd6556b28,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30ed6: mov 0x20(%rsp),%r11
0x0000000002b30edb: mov %r11,%r8
0x0000000002b30ede: movabs $0xd6556b28,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30ee8: mov %r8d,0x68(%r11)
0x0000000002b30eec: shr $0x9,%r10
0x0000000002b30ef0: mov $0x119ce000,%r11d
0x0000000002b30ef6: mov %r12b,(%r11,%r10,1) ;*putstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@24 (line 19)
0x0000000002b30efa: jmpq 0x0000000002b30e4b
0x0000000002b30eff: movabs $0x100060828,%rdx ; {metadata('com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30f09: xchg %ax,%ax
0x0000000002b30f0b: callq 0x0000000002a8f3e0 ; OopMap{off=336}
;*new ; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@17 (line 19)
; {runtime_call}
0x0000000002b30f10: jmp 0x0000000002b30ebb
0x0000000002b30f12: movabs $0xd6556b28,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30f1c: lock cmpxchg %r10,(%r11)
0x0000000002b30f21: movabs $0xd6556b28,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30f2b: lea 0x30(%rsp),%rbx
0x0000000002b30f30: mov (%r11),%rax
0x0000000002b30f33: test $0x2,%rax
0x0000000002b30f39: jne 0x0000000002b30f5f
0x0000000002b30f3b: or $0x1,%rax
0x0000000002b30f3f: mov %rax,(%rbx)
0x0000000002b30f42: lock cmpxchg %rbx,(%r11)
0x0000000002b30f47: je 0x0000000002b30f78
0x0000000002b30f4d: sub %rsp,%rax
0x0000000002b30f50: and $0xfffffffffffff007,%rax
0x0000000002b30f57: mov %rax,(%rbx)
0x0000000002b30f5a: jmpq 0x0000000002b30f78
0x0000000002b30f5f: movq $0x3,(%rbx)
0x0000000002b30f66: mov %rax,%rbx
0x0000000002b30f69: mov 0x16(%rbx),%rax
0x0000000002b30f6d: test %rax,%rax
0x0000000002b30f70: jne 0x0000000002b30f78
0x0000000002b30f72: lock cmpxchg %r15,0x16(%rbx)
0x0000000002b30f78: je 0x0000000002b30e36
0x0000000002b30f7e: movabs $0xd6556b28,%rdx ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30f88: lea 0x30(%rsp),%r8
0x0000000002b30f8d: xchg %ax,%ax
0x0000000002b30f8f: callq 0x0000000002a92220 ; OopMap{off=468}
;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
; {runtime_call}
0x0000000002b30f94: jmpq 0x0000000002b30e36
0x0000000002b30f99: test $0x7,%r8
0x0000000002b30fa0: jne 0x0000000002b30f12
0x0000000002b30fa6: test $0x300,%r8
0x0000000002b30fad: jne 0x0000000002b30fbc
0x0000000002b30faf: and $0x37f,%rax
0x0000000002b30fb6: mov %rax,%r11
0x0000000002b30fb9: or %r15,%r11
0x0000000002b30fbc: movabs $0xd6556b28,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30fc6: lock cmpxchg %r11,(%r10)
0x0000000002b30fcb: jne 0x0000000002b30f7e
0x0000000002b30fcd: jmpq 0x0000000002b30e36
0x0000000002b30fd2: movabs $0xd6556b28,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b30fdc: lea 0x30(%rsp),%rax
0x0000000002b30fe1: cmpq $0x0,(%rax)
0x0000000002b30fe8: je 0x0000000002b31062
0x0000000002b30fee: mov (%r11),%r10
0x0000000002b30ff1: test $0x2,%r10
0x0000000002b30ff8: je 0x0000000002b3105a
0x0000000002b30ffa: mov 0x16(%r10),%rax
0x0000000002b30ffe: xor %r15,%rax
0x0000000002b31001: or 0x26(%r10),%rax
0x0000000002b31005: jne 0x0000000002b31062
0x0000000002b31007: mov 0x36(%r10),%rax
0x0000000002b3100b: or 0x3e(%r10),%rax
0x0000000002b3100f: jne 0x0000000002b3101b
0x0000000002b31011: movq $0x0,0x16(%r10)
0x0000000002b31019: jmp 0x0000000002b31062
0x0000000002b3101b: cmpq $0x0,0x46(%r10)
0x0000000002b31023: je 0x0000000002b3104e
0x0000000002b31025: movq $0x0,0x16(%r10)
0x0000000002b3102d: lock addl $0x0,(%rsp)
0x0000000002b31032: cmpq $0x0,0x46(%r10)
0x0000000002b3103a: jne 0x0000000002b31053
0x0000000002b3103c: movabs $0x0,%rax
0x0000000002b31046: lock cmpxchg %r15,0x16(%r10)
0x0000000002b3104c: jne 0x0000000002b31053
0x0000000002b3104e: or $0x1,%eax
0x0000000002b31051: jmp 0x0000000002b31062
0x0000000002b31053: test $0x0,%eax
0x0000000002b31058: jmp 0x0000000002b31062
0x0000000002b3105a: mov (%rax),%r10
0x0000000002b3105d: lock cmpxchg %r10,(%r11)
0x0000000002b31062: je 0x0000000002b30e62
0x0000000002b31068: movabs $0xd6556b28,%rcx ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b31072: lea 0x30(%rsp),%rdx ;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
0x0000000002b31077: movabs $0x650fcce0,%r10
0x0000000002b31081: callq *%r10 ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@28 (line 21)
0x0000000002b31084: jmpq 0x0000000002b30e62 ;*new
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@17 (line 19)
0x0000000002b31089: mov %rax,%rbx
0x0000000002b3108c: jmp 0x0000000002b31091 ;*invokespecial
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@21 (line 19)
0x0000000002b3108e: mov %rax,%rbx
0x0000000002b31091: movabs $0xd6556b28,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b3109b: and (%r10),%rbp
0x0000000002b3109e: cmp $0x5,%rbp
0x0000000002b310a2: jne 0x0000000002b310b1 ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@34 (line 21)
0x0000000002b310a4: mov %rbx,%rdx
0x0000000002b310a7: add $0x40,%rsp
0x0000000002b310ab: pop %rbp
0x0000000002b310ac: jmpq 0x0000000002a92320 ; {runtime_call}
0x0000000002b310b1: movabs $0xd6556b28,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b310bb: lea 0x30(%rsp),%rax
0x0000000002b310c0: cmpq $0x0,(%rax)
0x0000000002b310c7: je 0x0000000002b31141
0x0000000002b310cd: mov (%r11),%r10
0x0000000002b310d0: test $0x2,%r10
0x0000000002b310d7: je 0x0000000002b31139
0x0000000002b310d9: mov 0x16(%r10),%rax
0x0000000002b310dd: xor %r15,%rax
0x0000000002b310e0: or 0x26(%r10),%rax
0x0000000002b310e4: jne 0x0000000002b31141
0x0000000002b310e6: mov 0x36(%r10),%rax
0x0000000002b310ea: or 0x3e(%r10),%rax
0x0000000002b310ee: jne 0x0000000002b310fa
0x0000000002b310f0: movq $0x0,0x16(%r10)
0x0000000002b310f8: jmp 0x0000000002b31141
0x0000000002b310fa: cmpq $0x0,0x46(%r10)
0x0000000002b31102: je 0x0000000002b3112d
0x0000000002b31104: movq $0x0,0x16(%r10)
0x0000000002b3110c: lock addl $0x0,(%rsp)
0x0000000002b31111: cmpq $0x0,0x46(%r10)
0x0000000002b31119: jne 0x0000000002b31132
0x0000000002b3111b: movabs $0x0,%rax
0x0000000002b31125: lock cmpxchg %r15,0x16(%r10)
0x0000000002b3112b: jne 0x0000000002b31132
0x0000000002b3112d: or $0x1,%eax
0x0000000002b31130: jmp 0x0000000002b31141
0x0000000002b31132: test $0x0,%eax
0x0000000002b31137: jmp 0x0000000002b31141
0x0000000002b31139: mov (%rax),%r10
0x0000000002b3113c: lock cmpxchg %r10,(%r11)
0x0000000002b31141: je 0x0000000002b310a4
0x0000000002b31147: movabs $0xd6556b28,%rcx ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002b31151: lea 0x30(%rsp),%rdx ;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
0x0000000002b31156: movabs $0x650fcce0,%r10
0x0000000002b31160: callq *%r10 ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@34 (line 21)
0x0000000002b31163: jmpq 0x0000000002b310a4
[Stub Code]
0x0000000002b31180: movabs $0x0,%rbx ; {no_reloc}
0x0000000002b3118a: jmpq 0x0000000002b3118a ; {runtime_call}
[Exception Handler]
0x0000000002b3118f: jmpq 0x0000000002a8f4a0 ; {runtime_call}
[Deopt Handler Code]
0x0000000002b31194: callq 0x0000000002b31199
0x0000000002b31199: subq $0x5,(%rsp)
0x0000000002b3119e: jmpq 0x0000000002a67600 ; {runtime_call}
0x0000000002b311a3: hlt
0x0000000002b311a4: hlt
0x0000000002b311a5: hlt
0x0000000002b311a6: hlt
0x0000000002b311a7: hlt
Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output
Process finished with exit code 0
把instance属性增加volatile关键字:
public volatile static VolatileSingleton instance;
重新执行main方法,控制台打印如下信息:
CompilerOracle: dontinline *VolatileSingleton.getInstance
CompilerOracle: compileonly *VolatileSingleton.getInstance
Loaded disassembler from C:\ProgramFiles\Java\jdk1.8.0_144\jre\bin\server\hsdis-amd64.dll
Decoding compiled method 0x0000000002f3d490:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000017352ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton'
# [sp+0x50] (sp of caller)
0x0000000002f3d620: mov %eax,-0x6000(%rsp)
0x0000000002f3d627: push %rbp
0x0000000002f3d628: sub $0x40,%rsp
0x0000000002f3d62c: movabs $0x17352c90,%rax ; {metadata(method data for {method} {0x0000000017352ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d636: mov 0xdc(%rax),%edx
0x0000000002f3d63c: add $0x8,%edx
0x0000000002f3d63f: mov %edx,0xdc(%rax)
0x0000000002f3d645: movabs $0x17352ab0,%rax ; {metadata({method} {0x0000000017352ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d64f: and $0x0,%edx
0x0000000002f3d652: cmp $0x0,%edx
0x0000000002f3d655: je 0x0000000002f3d90e
0x0000000002f3d65b: movabs $0xd6556ab8,%rax ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d665: mov 0x68(%rax),%eax ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@0 (line 16)
0x0000000002f3d668: cmp $0x0,%rax
0x0000000002f3d66c: movabs $0x17352c90,%rax ; {metadata(method data for {method} {0x0000000017352ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d676: movabs $0x108,%rdx
0x0000000002f3d680: jne 0x0000000002f3d690
0x0000000002f3d686: movabs $0x118,%rdx
0x0000000002f3d690: mov (%rax,%rdx,1),%rsi
0x0000000002f3d694: lea 0x1(%rsi),%rsi
0x0000000002f3d698: mov %rsi,(%rax,%rdx,1)
0x0000000002f3d69c: jne 0x0000000002f3d894 ;*ifnonnull
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@3 (line 16)
0x0000000002f3d6a2: movabs $0xd6556ab8,%rdx ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d6ac: lea 0x28(%rsp),%rsi
0x0000000002f3d6b1: mov %rdx,0x8(%rsi)
0x0000000002f3d6b5: mov (%rdx),%rax ; implicit exception: dispatches to 0x0000000002f3d925
0x0000000002f3d6b8: mov %rax,%rdi
0x0000000002f3d6bb: and $0x7,%rdi
0x0000000002f3d6bf: cmp $0x5,%rdi
0x0000000002f3d6c3: jne 0x0000000002f3d74a
0x0000000002f3d6c9: mov 0x8(%rdx),%edi
0x0000000002f3d6cc: shl $0x3,%rdi
0x0000000002f3d6d0: mov 0xa8(%rdi),%rdi
0x0000000002f3d6d7: or %r15,%rdi
0x0000000002f3d6da: xor %rax,%rdi
0x0000000002f3d6dd: and $0xffffffffffffff87,%rdi
0x0000000002f3d6e1: je 0x0000000002f3d772
0x0000000002f3d6e7: test $0x7,%rdi
0x0000000002f3d6ee: jne 0x0000000002f3d737
0x0000000002f3d6f0: test $0x300,%rdi
0x0000000002f3d6f7: jne 0x0000000002f3d716
0x0000000002f3d6f9: and $0x37f,%rax
0x0000000002f3d700: mov %rax,%rdi
0x0000000002f3d703: or %r15,%rdi
0x0000000002f3d706: lock cmpxchg %rdi,(%rdx)
0x0000000002f3d70b: jne 0x0000000002f3d92a
0x0000000002f3d711: jmpq 0x0000000002f3d772
0x0000000002f3d716: mov 0x8(%rdx),%edi
0x0000000002f3d719: shl $0x3,%rdi
0x0000000002f3d71d: mov 0xa8(%rdi),%rdi
0x0000000002f3d724: or %r15,%rdi
0x0000000002f3d727: lock cmpxchg %rdi,(%rdx)
0x0000000002f3d72c: jne 0x0000000002f3d92a
0x0000000002f3d732: jmpq 0x0000000002f3d772
0x0000000002f3d737: mov 0x8(%rdx),%edi
0x0000000002f3d73a: shl $0x3,%rdi
0x0000000002f3d73e: mov 0xa8(%rdi),%rdi
0x0000000002f3d745: lock cmpxchg %rdi,(%rdx)
0x0000000002f3d74a: mov (%rdx),%rax
0x0000000002f3d74d: or $0x1,%rax
0x0000000002f3d751: mov %rax,(%rsi)
0x0000000002f3d754: lock cmpxchg %rsi,(%rdx)
0x0000000002f3d759: je 0x0000000002f3d772
0x0000000002f3d75f: sub %rsp,%rax
0x0000000002f3d762: and $0xfffffffffffff007,%rax
0x0000000002f3d769: mov %rax,(%rsi)
0x0000000002f3d76c: jne 0x0000000002f3d92a ;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
0x0000000002f3d772: movabs $0xd6556ab8,%rdx ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d77c: mov 0x68(%rdx),%edx ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@11 (line 18)
0x0000000002f3d77f: cmp $0x0,%rdx
0x0000000002f3d783: movabs $0x17352c90,%rdx ; {metadata(method data for {method} {0x0000000017352ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d78d: movabs $0x128,%rsi
0x0000000002f3d797: jne 0x0000000002f3d7a7
0x0000000002f3d79d: movabs $0x138,%rsi
0x0000000002f3d7a7: mov (%rdx,%rsi,1),%rdi
0x0000000002f3d7ab: lea 0x1(%rdi),%rdi
0x0000000002f3d7af: mov %rdi,(%rdx,%rsi,1)
0x0000000002f3d7b3: jne 0x0000000002f3d849 ;*ifnonnull
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@14 (line 18)
0x0000000002f3d7b9: movabs $0x100060828,%rdx ; {metadata('com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d7c3: mov 0x60(%r15),%rax
0x0000000002f3d7c7: lea 0x10(%rax),%rdi
0x0000000002f3d7cb: cmp 0x70(%r15),%rdi
0x0000000002f3d7cf: ja 0x0000000002f3d93d
0x0000000002f3d7d5: mov %rdi,0x60(%r15)
0x0000000002f3d7d9: mov 0xa8(%rdx),%rcx
0x0000000002f3d7e0: mov %rcx,(%rax)
0x0000000002f3d7e3: mov %rdx,%rcx
0x0000000002f3d7e6: shr $0x3,%rcx
0x0000000002f3d7ea: mov %ecx,0x8(%rax)
0x0000000002f3d7ed: xor %rcx,%rcx
0x0000000002f3d7f0: mov %ecx,0xc(%rax)
0x0000000002f3d7f3: xor %rcx,%rcx ;*new ; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@17 (line 19)
0x0000000002f3d7f6: mov %rax,%rdx
0x0000000002f3d7f9: movabs $0x17352c90,%rsi ; {metadata(method data for {method} {0x0000000017352ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d803: addq $0x1,0x148(%rsi)
0x0000000002f3d80b: mov %rax,%rdx ;*invokespecial
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@21 (line 19)
0x0000000002f3d80e: mov %rax,0x20(%rsp)
0x0000000002f3d817: callq 0x0000000002e761a0 ; OopMap{[32]=Oop [48]=Oop off=508}
;*invokespecial
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@21 (line 19)
; {optimized virtual_call}
0x0000000002f3d81c: movabs $0xd6556ab8,%rax ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d826: mov 0x20(%rsp),%rsi
0x0000000002f3d82b: mov %rsi,%r10
0x0000000002f3d82e: mov %r10d,0x68(%rax)
0x0000000002f3d832: shr $0x9,%rax
0x0000000002f3d836: movabs $0x11dde000,%rsi
0x0000000002f3d840: movb $0x0,(%rax,%rsi,1)
0x0000000002f3d844: lock addl $0x0,(%rsp) ;*putstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@24 (line 19)
0x0000000002f3d849: movabs $0xd6556ab8,%rax ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d853: lea 0x28(%rsp),%rax
0x0000000002f3d858: mov 0x8(%rax),%rdi
0x0000000002f3d85c: mov (%rdi),%rsi
0x0000000002f3d85f: and $0x7,%rsi
0x0000000002f3d863: cmp $0x5,%rsi
0x0000000002f3d867: je 0x0000000002f3d884
0x0000000002f3d86d: mov (%rax),%rsi
0x0000000002f3d870: test %rsi,%rsi
0x0000000002f3d873: je 0x0000000002f3d884
0x0000000002f3d879: lock cmpxchg %rsi,(%rdi)
0x0000000002f3d87e: jne 0x0000000002f3d94a ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@28 (line 21)
0x0000000002f3d884: movabs $0x17352c90,%rax ; {metadata(method data for {method} {0x0000000017352ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d88e: incl 0x158(%rax) ;*goto
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@29 (line 21)
0x0000000002f3d894: movabs $0xd6556ab8,%rax ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d89e: mov 0x68(%rax),%eax ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@37 (line 23)
0x0000000002f3d8a1: add $0x40,%rsp
0x0000000002f3d8a5: pop %rbp
0x0000000002f3d8a6: test %eax,-0x24dd7ac(%rip) # 0x0000000000a60100
; {poll_return}
0x0000000002f3d8ac: retq ;*areturn
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@40 (line 23)
0x0000000002f3d8ad: mov 0x2a8(%r15),%rax
0x0000000002f3d8b4: xor %r10,%r10
0x0000000002f3d8b7: mov %r10,0x2a8(%r15)
0x0000000002f3d8be: xor %r10,%r10
0x0000000002f3d8c1: mov %r10,0x2b0(%r15)
0x0000000002f3d8c8: mov %rax,%rsi
0x0000000002f3d8cb: movabs $0xd6556ab8,%rax ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f3d8d5: lea 0x28(%rsp),%rax
0x0000000002f3d8da: mov 0x8(%rax),%rbx
0x0000000002f3d8de: mov (%rbx),%rdi
0x0000000002f3d8e1: and $0x7,%rdi
0x0000000002f3d8e5: cmp $0x5,%rdi
0x0000000002f3d8e9: je 0x0000000002f3d906
0x0000000002f3d8ef: mov (%rax),%rdi
0x0000000002f3d8f2: test %rdi,%rdi
0x0000000002f3d8f5: je 0x0000000002f3d906
0x0000000002f3d8fb: lock cmpxchg %rdi,(%rbx)
0x0000000002f3d900: jne 0x0000000002f3d95d ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@34 (line 21)
0x0000000002f3d906: mov %rsi,%rax
0x0000000002f3d909: jmpq 0x0000000002f3d998
0x0000000002f3d90e: mov %rax,0x8(%rsp)
0x0000000002f3d913: movq $0xffffffffffffffff,(%rsp)
0x0000000002f3d91b: callq 0x0000000002f32760 ; OopMap{off=768}
;*synchronization entry
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@-1 (line 16)
; {runtime_call}
0x0000000002f3d920: jmpq 0x0000000002f3d65b
0x0000000002f3d925: callq 0x0000000002ea1200 ; OopMap{rdx=Oop off=778}
;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
; {runtime_call}
0x0000000002f3d92a: mov %rdx,0x8(%rsp)
0x0000000002f3d92f: mov %rsi,(%rsp)
0x0000000002f3d933: callq 0x0000000002f30a60 ; OopMap{rdx=Oop [48]=Oop off=792}
;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
; {runtime_call}
0x0000000002f3d938: jmpq 0x0000000002f3d772
0x0000000002f3d93d: mov %rdx,%rdx
0x0000000002f3d940: callq 0x0000000002ea08c0 ; OopMap{[48]=Oop off=805}
;*new ; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@17 (line 19)
; {runtime_call}
0x0000000002f3d945: jmpq 0x0000000002f3d7f6
0x0000000002f3d94a: lea 0x28(%rsp),%rax
0x0000000002f3d94f: mov %rax,(%rsp)
0x0000000002f3d953: callq 0x0000000002f30e60 ; {runtime_call}
0x0000000002f3d958: jmpq 0x0000000002f3d884
0x0000000002f3d95d: lea 0x28(%rsp),%rax
0x0000000002f3d962: mov %rax,(%rsp)
0x0000000002f3d966: callq 0x0000000002f30e60 ; {runtime_call}
0x0000000002f3d96b: jmp 0x0000000002f3d906
0x0000000002f3d96d: nop
0x0000000002f3d96e: nop
0x0000000002f3d96f: mov 0x2a8(%r15),%rax
0x0000000002f3d976: movabs $0x0,%r10
0x0000000002f3d980: mov %r10,0x2a8(%r15)
0x0000000002f3d987: movabs $0x0,%r10
0x0000000002f3d991: mov %r10,0x2b0(%r15)
0x0000000002f3d998: add $0x40,%rsp
0x0000000002f3d99c: pop %rbp
0x0000000002f3d99d: jmpq 0x0000000002e9fbe0 ; {runtime_call}
[Stub Code]
0x0000000002f3d9c0: nop ; {no_reloc}
0x0000000002f3d9c5: movabs $0x0,%rbx ; {static_stub}
0x0000000002f3d9cf: jmpq 0x0000000002f3d9cf ; {runtime_call}
[Exception Handler]
0x0000000002f3d9d4: callq 0x0000000002ea2b20 ; {runtime_call}
0x0000000002f3d9d9: mov %rsp,-0x28(%rsp)
0x0000000002f3d9de: sub $0x80,%rsp
0x0000000002f3d9e5: mov %rax,0x78(%rsp)
0x0000000002f3d9ea: mov %rcx,0x70(%rsp)
0x0000000002f3d9ef: mov %rdx,0x68(%rsp)
0x0000000002f3d9f4: mov %rbx,0x60(%rsp)
0x0000000002f3d9f9: mov %rbp,0x50(%rsp)
0x0000000002f3d9fe: mov %rsi,0x48(%rsp)
0x0000000002f3da03: mov %rdi,0x40(%rsp)
0x0000000002f3da08: mov %r8,0x38(%rsp)
0x0000000002f3da0d: mov %r9,0x30(%rsp)
0x0000000002f3da12: mov %r10,0x28(%rsp)
0x0000000002f3da17: mov %r11,0x20(%rsp)
0x0000000002f3da1c: mov %r12,0x18(%rsp)
0x0000000002f3da21: mov %r13,0x10(%rsp)
0x0000000002f3da26: mov %r14,0x8(%rsp)
0x0000000002f3da2b: mov %r15,(%rsp)
0x0000000002f3da2f: movabs $0x65509e40,%rcx ; {external_word}
0x0000000002f3da39: movabs $0x2f3d9d9,%rdx ; {internal_word}
0x0000000002f3da43: mov %rsp,%r8
0x0000000002f3da46: and $0xfffffffffffffff0,%rsp
0x0000000002f3da4a: callq 0x00000000651c3cf0 ; {runtime_call}
0x0000000002f3da4f: hlt
[Deopt Handler Code]
0x0000000002f3da50: movabs $0x2f3da50,%r10 ; {section_word}
0x0000000002f3da5a: push %r10
0x0000000002f3da5c: jmpq 0x0000000002e77600 ; {runtime_call}
Decoding compiled method 0x0000000002f40c90:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000017352ab8} 'getInstance' '()Lcom/sunny/concurrent/volatilekey/VolatileSingleton;' in 'com/sunny/concurrent/volatilekey/VolatileSingleton'
# [sp+0x50] (sp of caller)
0x0000000002f40e00: mov %eax,-0x6000(%rsp)
0x0000000002f40e07: push %rbp
0x0000000002f40e08: sub $0x40,%rsp ;*synchronization entry
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@-1 (line 16)
0x0000000002f40e0c: movabs $0xd6556ab8,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40e16: mov 0x68(%r10),%r11d ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@0 (line 16)
0x0000000002f40e1a: test %r11d,%r11d
0x0000000002f40e1d: je 0x0000000002f40e3c ;*ifnonnull
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@3 (line 16)
0x0000000002f40e1f: movabs $0xd6556ab8,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40e29: mov 0x68(%r10),%r10d
0x0000000002f40e2d: mov %r10,%rax ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@37 (line 23)
0x0000000002f40e30: add $0x40,%rsp
0x0000000002f40e34: pop %rbp
0x0000000002f40e35: test %eax,-0x24e0e3b(%rip) # 0x0000000000a60000
; {poll_return}
0x0000000002f40e3b: retq
0x0000000002f40e3c: mov (%r10),%rax
0x0000000002f40e3f: mov %rax,%r10
0x0000000002f40e42: and $0x7,%r10
0x0000000002f40e46: cmp $0x5,%r10
0x0000000002f40e4a: jne 0x0000000002f41011
0x0000000002f40e50: mov $0x200003df,%r11d ; {metadata('java/lang/Class')}
0x0000000002f40e56: movabs $0x0,%r10
0x0000000002f40e60: lea (%r10,%r11,8),%r10
0x0000000002f40e64: mov 0xa8(%r10),%r10
0x0000000002f40e6b: mov %r10,%r11
0x0000000002f40e6e: or %r15,%r11
0x0000000002f40e71: mov %r11,%r8
0x0000000002f40e74: xor %rax,%r8
0x0000000002f40e77: test $0xffffffffffffff87,%r8
0x0000000002f40e7e: jne 0x0000000002f41089 ;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
0x0000000002f40e84: movabs $0xd6556ab8,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40e8e: mov 0x68(%r10),%r10d ;*getstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@11 (line 18)
0x0000000002f40e92: mov $0x7,%ebp
0x0000000002f40e97: test %r10d,%r10d
0x0000000002f40e9a: je 0x0000000002f40f6e ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@28 (line 21)
0x0000000002f40ea0: movabs $0xd6556ab8,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40eaa: and (%r10),%rbp
0x0000000002f40ead: cmp $0x5,%rbp
0x0000000002f40eb1: je 0x0000000002f40e1f
0x0000000002f40eb7: movabs $0xd6556ab8,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40ec1: lea 0x30(%rsp),%rax
0x0000000002f40ec6: cmpq $0x0,(%rax)
0x0000000002f40ecd: je 0x0000000002f40f47
0x0000000002f40ed3: mov (%r11),%r10
0x0000000002f40ed6: test $0x2,%r10
0x0000000002f40edd: je 0x0000000002f40f3f
0x0000000002f40edf: mov 0x16(%r10),%rax
0x0000000002f40ee3: xor %r15,%rax
0x0000000002f40ee6: or 0x26(%r10),%rax
0x0000000002f40eea: jne 0x0000000002f40f47
0x0000000002f40eec: mov 0x36(%r10),%rax
0x0000000002f40ef0: or 0x3e(%r10),%rax
0x0000000002f40ef4: jne 0x0000000002f40f00
0x0000000002f40ef6: movq $0x0,0x16(%r10)
0x0000000002f40efe: jmp 0x0000000002f40f47
0x0000000002f40f00: cmpq $0x0,0x46(%r10)
0x0000000002f40f08: je 0x0000000002f40f33
0x0000000002f40f0a: movq $0x0,0x16(%r10)
0x0000000002f40f12: lock addl $0x0,(%rsp)
0x0000000002f40f17: cmpq $0x0,0x46(%r10)
0x0000000002f40f1f: jne 0x0000000002f40f38
0x0000000002f40f21: movabs $0x0,%rax
0x0000000002f40f2b: lock cmpxchg %r15,0x16(%r10)
0x0000000002f40f31: jne 0x0000000002f40f38
0x0000000002f40f33: or $0x1,%eax
0x0000000002f40f36: jmp 0x0000000002f40f47
0x0000000002f40f38: test $0x0,%eax
0x0000000002f40f3d: jmp 0x0000000002f40f47
0x0000000002f40f3f: mov (%rax),%r10
0x0000000002f40f42: lock cmpxchg %r10,(%r11)
0x0000000002f40f47: je 0x0000000002f40e1f
0x0000000002f40f4d: movabs $0xd6556ab8,%rcx ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40f57: lea 0x30(%rsp),%rdx ;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
0x0000000002f40f5c: movabs $0x650fcce0,%r10
0x0000000002f40f66: callq *%r10 ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@28 (line 21)
0x0000000002f40f69: jmpq 0x0000000002f40e1f
0x0000000002f40f6e: mov 0x60(%r15),%rax
0x0000000002f40f72: mov %rax,%r10
0x0000000002f40f75: add $0x10,%r10
0x0000000002f40f79: cmp 0x70(%r15),%r10
0x0000000002f40f7d: jae 0x0000000002f40ff1
0x0000000002f40f7f: mov %r10,0x60(%r15)
0x0000000002f40f83: prefetchw 0xc0(%r10)
0x0000000002f40f8b: mov $0x2000c105,%r10d ; {metadata('com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40f91: shl $0x3,%r10
0x0000000002f40f95: mov 0xa8(%r10),%r10
0x0000000002f40f9c: mov %r10,(%rax)
0x0000000002f40f9f: movl $0x2000c105,0x8(%rax) ; {metadata('com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40fa6: mov %r12d,0xc(%rax)
0x0000000002f40faa: mov %rax,%r10
0x0000000002f40fad: mov %r10,0x20(%rsp) ;*new ; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@17 (line 19)
0x0000000002f40fb2: mov %r10,%rdx
0x0000000002f40fb5: xchg %ax,%ax
0x0000000002f40fb7: callq 0x0000000002e761a0 ; OopMap{[32]=Oop off=444}
;*invokespecial
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@21 (line 19)
; {optimized virtual_call}
0x0000000002f40fbc: mov 0x20(%rsp),%r10
0x0000000002f40fc1: movabs $0xd6556ab8,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40fcb: mov %r10d,0x68(%r11)
0x0000000002f40fcf: movabs $0xd6556ab8,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40fd9: shr $0x9,%r10
0x0000000002f40fdd: mov $0x11dde000,%r11d
0x0000000002f40fe3: mov %r12b,(%r11,%r10,1)
0x0000000002f40fe7: lock addl $0x0,(%rsp) ;*putstatic instance
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@24 (line 19)
0x0000000002f40fec: jmpq 0x0000000002f40ea0
0x0000000002f40ff1: movabs $0x100060828,%rdx ; {metadata('com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f40ffb: callq 0x0000000002e9f3e0 ; OopMap{off=512}
;*new ; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@17 (line 19)
; {runtime_call}
0x0000000002f41000: jmp 0x0000000002f40faa
0x0000000002f41002: movabs $0xd6556ab8,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f4100c: lock cmpxchg %r10,(%r11)
0x0000000002f41011: movabs $0xd6556ab8,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f4101b: lea 0x30(%rsp),%rbx
0x0000000002f41020: mov (%r11),%rax
0x0000000002f41023: test $0x2,%rax
0x0000000002f41029: jne 0x0000000002f4104f
0x0000000002f4102b: or $0x1,%rax
0x0000000002f4102f: mov %rax,(%rbx)
0x0000000002f41032: lock cmpxchg %rbx,(%r11)
0x0000000002f41037: je 0x0000000002f41068
0x0000000002f4103d: sub %rsp,%rax
0x0000000002f41040: and $0xfffffffffffff007,%rax
0x0000000002f41047: mov %rax,(%rbx)
0x0000000002f4104a: jmpq 0x0000000002f41068
0x0000000002f4104f: movq $0x3,(%rbx)
0x0000000002f41056: mov %rax,%rbx
0x0000000002f41059: mov 0x16(%rbx),%rax
0x0000000002f4105d: test %rax,%rax
0x0000000002f41060: jne 0x0000000002f41068
0x0000000002f41062: lock cmpxchg %r15,0x16(%rbx)
0x0000000002f41068: je 0x0000000002f40e84
0x0000000002f4106e: movabs $0xd6556ab8,%rdx ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f41078: lea 0x30(%rsp),%r8
0x0000000002f4107d: xchg %ax,%ax
0x0000000002f4107f: callq 0x0000000002ea0160 ; OopMap{off=644}
;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
; {runtime_call}
0x0000000002f41084: jmpq 0x0000000002f40e84
0x0000000002f41089: test $0x7,%r8
0x0000000002f41090: jne 0x0000000002f41002
0x0000000002f41096: test $0x300,%r8
0x0000000002f4109d: jne 0x0000000002f410ac
0x0000000002f4109f: and $0x37f,%rax
0x0000000002f410a6: mov %rax,%r11
0x0000000002f410a9: or %r15,%r11
0x0000000002f410ac: movabs $0xd6556ab8,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f410b6: lock cmpxchg %r11,(%r10)
0x0000000002f410bb: jne 0x0000000002f4106e
0x0000000002f410bd: jmpq 0x0000000002f40e84 ;*new
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@17 (line 19)
0x0000000002f410c2: mov %rax,%rbx
0x0000000002f410c5: jmp 0x0000000002f410ca ;*invokespecial
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@21 (line 19)
0x0000000002f410c7: mov %rax,%rbx
0x0000000002f410ca: movabs $0xd6556ab8,%r10 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f410d4: and (%r10),%rbp
0x0000000002f410d7: cmp $0x5,%rbp
0x0000000002f410db: jne 0x0000000002f410ea ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@34 (line 21)
0x0000000002f410dd: mov %rbx,%rdx
0x0000000002f410e0: add $0x40,%rsp
0x0000000002f410e4: pop %rbp
0x0000000002f410e5: jmpq 0x0000000002ea0060 ; {runtime_call}
0x0000000002f410ea: movabs $0xd6556ab8,%r11 ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f410f4: lea 0x30(%rsp),%rax
0x0000000002f410f9: cmpq $0x0,(%rax)
0x0000000002f41100: je 0x0000000002f4117a
0x0000000002f41106: mov (%r11),%r10
0x0000000002f41109: test $0x2,%r10
0x0000000002f41110: je 0x0000000002f41172
0x0000000002f41112: mov 0x16(%r10),%rax
0x0000000002f41116: xor %r15,%rax
0x0000000002f41119: or 0x26(%r10),%rax
0x0000000002f4111d: jne 0x0000000002f4117a
0x0000000002f4111f: mov 0x36(%r10),%rax
0x0000000002f41123: or 0x3e(%r10),%rax
0x0000000002f41127: jne 0x0000000002f41133
0x0000000002f41129: movq $0x0,0x16(%r10)
0x0000000002f41131: jmp 0x0000000002f4117a
0x0000000002f41133: cmpq $0x0,0x46(%r10)
0x0000000002f4113b: je 0x0000000002f41166
0x0000000002f4113d: movq $0x0,0x16(%r10)
0x0000000002f41145: lock addl $0x0,(%rsp)
0x0000000002f4114a: cmpq $0x0,0x46(%r10)
0x0000000002f41152: jne 0x0000000002f4116b
0x0000000002f41154: movabs $0x0,%rax
0x0000000002f4115e: lock cmpxchg %r15,0x16(%r10)
0x0000000002f41164: jne 0x0000000002f4116b
0x0000000002f41166: or $0x1,%eax
0x0000000002f41169: jmp 0x0000000002f4117a
0x0000000002f4116b: test $0x0,%eax
0x0000000002f41170: jmp 0x0000000002f4117a
0x0000000002f41172: mov (%rax),%r10
0x0000000002f41175: lock cmpxchg %r10,(%r11)
0x0000000002f4117a: je 0x0000000002f410dd
0x0000000002f41180: movabs $0xd6556ab8,%rcx ; {oop(a 'java/lang/Class' = 'com/sunny/concurrent/volatilekey/VolatileSingleton')}
0x0000000002f4118a: lea 0x30(%rsp),%rdx ;*monitorenter
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@10 (line 17)
0x0000000002f4118f: movabs $0x650fcce0,%r10
0x0000000002f41199: callq *%r10 ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@34 (line 21)
0x0000000002f4119c: jmpq 0x0000000002f410dd ;*monitorexit
; - com.sunny.concurrent.volatilekey.VolatileSingleton::getInstance@28 (line 21)
[Stub Code]
0x0000000002f411c0: movabs $0x0,%rbx ; {no_reloc}
0x0000000002f411ca: jmpq 0x0000000002f411ca ; {runtime_call}
[Exception Handler]
0x0000000002f411cf: jmpq 0x0000000002e9f4a0 ; {runtime_call}
[Deopt Handler Code]
0x0000000002f411d4: callq 0x0000000002f411d9
0x0000000002f411d9: subq $0x5,(%rsp)
0x0000000002f411de: jmpq 0x0000000002e77600 ; {runtime_call}
0x0000000002f411e3: hlt
0x0000000002f411e4: hlt
0x0000000002f411e5: hlt
0x0000000002f411e6: hlt
0x0000000002f411e7: hlt
Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output
Process finished with exit code 0
使用文本比较器进行对比,我们便可以知道带volatile关键字和不带的差异:带了volatile关键字汇编指令会比不带volatile关键多了lock addl $0x0,(%rsp)
操作;
这个操作相当于一个内存屏障,只有一个 CPU 访问内存时,并不需要内存屏障;但如果有两个或更多 CPU 访问同一块内存,且其中有一个在观测另一个,就需要内存屏障来保证一致性了。指令
lock addl $0x0,(%esp)
显然是一个空操作,关键在于 lock 前缀,查询 IA32 手册,它的作用是使得本 CPU 的 Cache 写入了内存,该写入动作也会引起别的 CPU invalidate 其 Cache。所以通过这样一个空操作,可让前面 volatile 变量的修改对其他 CPU 立即可见。
声言处理器的 LOCK# 信号(使指令成为原子指令)。在多处理器环境中,LOCK# 信号确保在声言该信号期间,处理器可以独占使用任何共享内存。
请注意,在后期的“英特尔(R) 体系结构”处理器(如奔腾® Pro 处理器)中,锁定可能会在没有声言 LOCK# 信号的情况下发生。请参阅下面的“英特尔(R) 体系结构兼容性”。
LOCK 前缀只能用在以下指令的前面,并且仅限使用内存操作数形式的这些指令:ADD、ADC、AND、BTC、BTR、BTS、CMPXCHG、DEC、INC、NEG、NOT、OR、SBB、SUB、XOR、XADD 及 XCHG。如果同其它任何指令一起使用 LOCK 前缀,则生成操作码未定义异常。不论是否使用 LOCK 前缀,XCHG 指令总是声言 LOCK# 信号。
LOCK 前缀通常同 BTS 指令一起使用,以便在共享内存环境中读取-修改-写入内存位置。
内存字段是否对齐不影响 LOCK 前缀的完整性。对于杂乱的未对齐字段,会遵循内存锁定规则。
五、参考引用
- 汪文君《Java高并发编程详解多线程与架构设计》
- 《并发研究之CPU缓存一致性协议(MESI)》