简单谈谈什么是指令重排序?

前言
相信大家都知道final、volatile都有禁止指令重排序的作用,但究竟什么是指令重排序呢?

一、什么是指令重排序?
比如我们写了一个 Java 程序,我们一般都会认为,程序中语句实际的执行顺序和我们所写的代码顺序是一致的。
但实际上,编译器、JVM 或者 CPU 都有可能为了优化代码执行效率,在不改变原有代码语义的基础之上,对指令的实际执行顺序进行调整,这就是指令重排序。

二、指令重排序案例
在这里插入图片描述
1、上图左侧是 3 行 Java 代码,右侧是这 3 行代码可能被转化后的指令。
2、可以看出 a = 100 对应的是 Load a、Set to 100、Store a,意味着从主存中读取 a 的值,然后把值设置为 100,并写回主存。同理, b = 5 对应的是下面三行 Load b、Set to 5、Store b,最后的 a = a + 10,对应的是 Load a、Set to 110、Store a。
3、不难发现这里有两次“Load a”和两次“Store a”,那么可不可以在不改变上面代码原有语义的基础上,对代码实际的执行顺序进行优化,从而提高我们程序的执行效率呢?这显然是可以的,也就是去进行指令重排序。

指令重排序之后的情况:
在这里插入图片描述
1、重排序之后, 对变量a 的两次操作被放到了一起,指令执行情况变为 Load a、Set to 100、Set to 110、 Store a。
2、下面对 变量b 相关的指令不变,仍对应 Load b、 Set to 5、Store b。
3、可以看到在重排序后,对变量 a 的相关指令发生了变化,即节省了一次 Load a 和一次 Store a。
4、不难发现,指令重排序通过减少执行指令,提高了程序整体的运行效率,这就是重排序带来的优化和好处。

重排序的 3 种情况

(1)编译器优化
编译器(包括 JVM、JIT 编译器等)出于优化的目的,例如当前有了数据 a,把对 a 的操作放到一起效率会更高,避免读取 b 后又返回来重新读取 a 的时间开销,此时在编译的过程中会进行一定程度的重排。不过重排序并不意味着可以任意排序,它需要需要保证重排序后,不改变单线程内的语义,否则如果能任意排序的话,程序早就逻辑混乱了。

(2)CPU 重排序
CPU 同样会有优化行为,这里的优化和编译器优化类似,都是通过乱序执行的技术来提高整体的执行效率。
所以即使之前编译器不发生重排,CPU 也可能进行重排,我们在开发中,一定要考虑到重排序带来的后果。

(3) 内存的“重排序”
内存系统内不存在真正的重排序,但是内存会带来看上去和重排序一样的效果,所以这里的“重排序”打了双引号。
由于内存有缓存的存在,在 JMM 里表现为主存和本地内存,而主存和本地内存的内容可能不一致,所以这也会导致程序表现出乱序的行为。
举个例子,线程 1 修改了 a 的值,但是修改后没有来得及把新结果写回主存,或者线程 2 没来得及读到最新的值,那么线程 2就 看不到刚才线程 1 对 a 的修改,此时线程 2 看到的 a 还是初始值。但是线程 2 却可能看到线程 1 修改 a 之后的代码执行效果,表面上看起来像是发生了重排序。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值