刚刚在研究volatile变量的时候,涉及到重排序的概念,于是发现了这篇很好的文章,写得很简短很明白。所以转载一下。

原文地址:JAVA中JVM的重排序详细介绍

原文贴出来:

重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境。

在并发程序中,程序员会特别关注不同进程或线程之间的数据同步,特别是多个线程同时修改同一变量时,必须采取可靠的同步或其它措施保障数据被正确地修改,这里的一条重要原则是:不要假设指令执行的顺序,你无法预知不同线程之间的指令会以何种顺序执行。

但是在单线程程序中,通常我们容易假设指令是顺序执行的,否则可以想象程序会发生什么可怕的变化。理想的模型是:各种指令执行的顺序是唯一且有序的,这个顺序就是它们被编写在代码中的顺序,与处理器或其它因素无关,这种模型被称作顺序一致性模型,也是基于冯·诺依曼体系的模型。当然,这种假设本身是合理的,在实践中也鲜有异常发生,但事实上,没有哪个现代多处理器架构会采用这种模型,因为它是在是太低效了。而在编译优化和CPU流水线中,几乎都涉及到指令重排序。

编译期重排序

编译期重排序的典型就是通过调整指令顺序,在不改变程序语义的前提下,尽可能减少寄存器的读取、存储次数,充分复用寄存器的存储值。

假设第一条指令计算一个值赋给变量A并存放在寄存器中,第二条指令与A无关但需要占用寄存器(假设它将占用A所在的那个寄存器),第三条指令使用A的值且与第二条指令无关。那么如果按照顺序一致性模型,A在第一条指令执行过后被放入寄存器,在第二条指令执行时A不再存在,第三条指令执行时A重新被读入寄存器,而这个过程中,A的值没有发生变化。通常编译器都会交换第二和第三条指令的位置,这样第一条指令结束时A存在于寄存器中,接下来可以直接从寄存器中读取A的值,降低了重复读取的开销。

重排序对于流水线的意义

现代CPU几乎都采用流水线机制加快指令的处理速度,一般来说,一条指令需要若干个CPU时钟周期处理,而通过流水线并行执行,可以在同等的时钟周期内执行若干条指令,具体做法简单地说就是把指令分为不同的执行周期,例如读取、寻址、解析、执行等步骤,并放在不同的元件中处理,同时在执行单元EU中,功能单元被分为不同的元件,例如加法元件、乘法元件、加载元件、存储元件等,可以进一步实现不同的计算并行执行。

流水线架构决定了指令应该被并行执行,而不是在顺序化模型中所认为的那样。重排序有利于充分使用流水线,进而达到超标量的效果。

确保顺序性

尽管指令在执行时并不一定按照我们所编写的顺序执行,但毋庸置疑的是,在单线程环境下,指令执行的最终效果应当与其在顺序执行下的效果一致,否则这种优化便会失去意义。

通常无论是在编译期还是运行期进行的指令重排序,都会满足上面的原则。

Java存储模型中的重排序

在Java存储模型(Java Memory Model, JMM)中,重排序是十分重要的一节,特别是在并发编程中。JMM通过happens-before法则保证顺序执行语义,如果想要让执行操作B的线程观察到执行操作A的线程的结果,那么A和B就必须满足happens-before原则,否则,JVM可以对它们进行任意排序以提高程序性能。

volatile关键字可以保证变量的可见性,因为对volatile的操作都在Main Memory中,而Main Memory是被所有线程所共享的,这里的代价就是牺牲了性能,无法利用寄存器或Cache,因为它们都不是全局的,无法保证可见性,可能产生脏读。

volatile还有一个作用就是局部阻止重排序的发生,对volatile变量的操作指令都不会被重排序,因为如果重排序,又可能产生可见性问题。

在保证可见性方面,锁(包括显式锁、对象锁)以及对原子变量的读写都可以确保变量的可见性。但是实现方式略有不同,例如同步锁保证得到锁时从内存里重新读入数据刷新缓存,释放锁时将数据写回内存以保数据可见,而volatile变量干脆都是读写内存。

JAVA中JVM的重排序详细介绍(写得很明白)相关推荐

  1. java 链接重排序_JAVA中JVM的重排序详细介绍

    重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段.重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境 在并发程序中,程序员会特别关注不同进程或 ...

  2. Java中的字符串常量池详细介绍

    Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...

  3. java中的vm自变量_java中JVM虚拟机内存模型详细说明

    java中JVM虚拟机内存模型详细说明 JVM的内部结构如下图: 一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求 ...

  4. 由Java引起的指令重排序思考

    背景 问题出现 最近遇到了一个NullPointerException,虽然量不大,但是很怪异,大致长这个样子 这是个什么空指针?居然说我LinkedList.iterator().hasNext() ...

  5. 形式参数内存在哪java_深入浅出Java中JVM内存管理

    原标题:深入浅出Java中JVM内存管理 Java岗位面试,JVM是对程序员基本功考察,通常会问你对JVM了解吗?可以分几部分回答这个问题,首先JVM内存划分 | JVM垃圾回收的含义 | 有哪些GC ...

  6. java 实体属性个数_?Java中比较实用实体转换工具介绍

    ​Java中比较实用实体转换工具介绍 文中源码地址 大家一般编码过程中,经常会遇到DO对象转化为DTO对象,对象和对象之间转换一般需要用到转换工具,毕竟使用getter/setter太过麻烦 DO:D ...

  7. Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例

    转载自  Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例 第1部分 ArrayList介绍 ArrayList简介 ArrayList 是一个数组队列,相当于 动态数组.与 ...

  8. Java中图形界面重绘方法

    ** Java中图形界面重绘方法 **   在我们编写图形界面程序的过程中,我们就会发现,当我们将窗体拉伸,缩小(或者最大化最小化)的时候,之前在窗体上画的图形会消失了. 这是为什么呢?   1)原来 ...

  9. java中如何对对象排序?

    大家好,我是雄雄. 前言: 我们知道,在平时做项目的过程中,我们总会用到各种各样的排序,或是升序,或是降序.在java中,要实现排序有好多中方式,比如我们耳熟能详的冒泡排序.选择排序等,但是我们一般都 ...

最新文章

  1. idea php 断点设置,php - xdebug在IntelliJ Idea中跳过断点 - SO中文参考 - www.soinside.com...
  2. reactjs虚拟DOM与真实DOM
  3. 16.6 创建测试数据
  4. bootstrap内容部分API解读(1)
  5. Cracking the Coding Interview 5.2
  6. 如何对mysql做物理备份_如何创建物理MySQL备份
  7. 翻转子串(important!)
  8. iOS求职之OC面试题完整版---持续更新中...
  9. python编程是啥-小学生都开始学的Python编程到底是什么?
  10. c语言判断奇偶素数,用C语言如何判断素数
  11. luov之IT管理制度
  12. mysql索引详细介绍简书_细说mysql索引
  13. iOS好用的第三方框架/插件
  14. 快速排序的三种方式以及快排的优化
  15. (unix网络编程)即时通讯工具二:服务端与客户端融合
  16. linux服务器怎么搭建简单的网站?linux搭建网站教程
  17. Android APK签名总结-- V1签名和V2签名使用和区别
  18. 查看计算机温度指令,电脑硬件温度如何查看
  19. python泰勒公式_Python用泰勒公式模拟函数
  20. UI设计教程分享:6个不能错过的UI设计网站

热门文章

  1. Android调用系统自带的文件管理器进行文件选择
  2. 南理工校外调剂计算机有消息,提醒!这些学校已经开启预调剂了!
  3. 认识V模型、W模型、H模型
  4. KKT (LICQ)
  5. 【图像配准】SIFT算法原理及二图配准拼接
  6. 西安电子科技大学经济与管理学院861上岸学姐考研经验分享
  7. Jackson注解:@JsonAlias使用详解
  8. 毕业设计EMS办公管理系统(B/S结构)+J2EE+SQLserver8.0
  9. 网络基础:(二)路由选择基础与静态路由
  10. java8的时期和时间