一、重排序

重排序是指为了提高程序的执行效率,编译器和处理器常常会对语句的执行顺序或者指令的执行顺序进行重排。

编译器优化的重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。

指令级并行的重排序:现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。

内存系统的重排序:由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

从java源代码到最终实际执行的指令序列,会分别经历下面三种重排序:

Java源代码--->1编译器重排-->2指令集重排-->3内存系统级重排序--->最终的程序执行代码

其中1属于编译器重排,2,3是处理器级别的重排。多线程中各种操作延迟或者看似乱序执行的原因都可以归为重排序。

对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序(不是所有的编译器重排序都要禁止)。对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)。

JMM属于语言级的内存模型,它确保在不同的编译器和不同的处理器平台之上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。

二、处理器重排序

现代的处理器使用写缓冲区来临时保存向内存写入的数据。写缓冲区可以保证指令流水线持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时,通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,可以减少对内存总线的占用。虽然写缓冲区有这么多好处,但每个处理器上的写缓冲区,仅仅对它所在的处理器可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的读/写操作的执行顺序,不一定与内存实际发生的读/写操作顺序一致!

三、、内存屏障指令

为了保证内存可见性,java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。JMM把内存屏障指令分为下列四类:

StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他三个屏障的效果。现代的多处理器大都支持该屏障(其他类型的屏障不一定被所有处理器支持)。执行该屏障开销会很昂贵,因为当前处理器通常要把写缓冲区中的数据全部刷新到内存中(buffer fully flush)。

三、数据依赖

如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分下列三种类型:

前面提到过,编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。

上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变。

注意,这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。

四、as-if-serial语义

as-if-serial语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime和处理器都必须遵守as-if-serial语义。为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序

java 排序 内存_Java内存模型(二)——重排序相关推荐

  1. Java并发篇_Java内存模型

    在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题.那么它们产生的原因和在Java中解决的办法又是什么呢? 一.内存模型的相关概念 ​ 计算机在执行程序时,每条指令都是在CP ...

  2. java升序问题_JAVA并发理解之重排序问题

    首先我们先来了解一下什么是重排序:重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段. 从Java源代码到最终实际执行的指令序列,会分别经历下面3种重排序,如下图所示 上述的1 ...

  3. java model 原则_java内存模型(Java Memeory Model)

    1.java内存模型的重要目标是定义程序中各个访问变量的访问规则,即在虚拟机中将变量存储到内存,和从内存中取出变量的低层细节:这里所说的变量主要包括实例字段,静态字段,构成数组对象的元素不包括局部变量 ...

  4. java锁上升_Java内存模型FAQ(十一)新的内存模型是否修复了双重锁检查问题?...

    臭名昭著的双重锁检查(也叫多线程单例模式)是一个骗人的把戏,它用来支持lazy初始化,同时避免过度使用同步.在非常早的JVM中,同步非常慢,开发人员非常希望删掉它.双重锁检查代码如下: // doub ...

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

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

  6. java 句柄 内存_Java内存区域学习

    运行时区域 Java虚拟机在执行Java程序过程中把其所管理的内存划分成若干个不同的数据区域. 程序计数器 当前线程所执行的字节码的指示器.通过改变这个计数器的值来选取下一个需要执行的字节码指令,分支 ...

  7. java 及时释放内存_Java 内存释放

     问题一什么叫垃圾回收机制 垃圾回收是一种动态存储管理技术它自动地释放不再被程序引用的对象按照特定的垃圾收集算法来实现资源自动回收的功能.当一个对象不再被引用的时候内存回收它占领的空间 ...

  8. java 查看内存_java 内存查看工具

    业界有很多强大的java profile的工具,比如Jporfiler,yourkit,这些收费的东西我就不想说了,想说的是,其实java自己就提供了很多内存监控的小工具,下面列举的工具只是一小部分, ...

  9. java 分析内存_Java 内存查看与分析

    1:gc日志输出 在jvm启动参数中加入 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimestamps -XX:+PrintGCApplication ...

最新文章

  1. 童心制物(Makeblock)受邀参加2020年韩国机器人世界展览会,倡导以先进的STEAM教育培养未来复合型人才
  2. JavaScript核心语法学习部分(四)
  3. Android之系统自带的文字外观设置
  4. Android log 里面快速搜索错误堆栈 ( 关键字)
  5. python基础知识资料-Python基础知识快速学习系列视频课程
  6. ViewPager详解(一)——ViewPager的基本使用完整示例
  7. python中的栈结构_对Python列表进行封装和二次开发实现自定义栈结构
  8. 《精通Python设计模式》学习结构型之享元模式
  9. Hive的下载安装,以及配置mysql作为元数据库
  10. 配置MySQL5.7基于keepalived的GTID的双主复制
  11. 如何启动和关闭oracle数据库,Oracle数据库启动和关闭方式总结
  12. atitit.设计文档---操作日志的实现
  13. 无论发生了什么生活_无论如何,一个开放团队的经理会做什么?
  14. 为何new出的对象数组必须要用delete[]删除,而普通数组delete和delete[]都一样-------_CrtMemBlockHeader
  15. Badger DAO公布系统乘数奖励机制,新增时间加权奖励
  16. 宽字符与Unicode
  17. 动易模板制作示例(二)
  18. 应用程序错误(0xc0000135)
  19. 什么是外汇EA呢?工作的原理又是什么呢?送给不懂外汇EA
  20. 考研作文--过去数年见证了一个社会现象

热门文章

  1. 《长安十二时辰》火了!程序员版本过于真实!
  2. Facebook 发币 Libra;谷歌十亿美金为穷人造房;第四代树莓派 Raspberry Pi 4 发布 | 开发者周刊...
  3. 我是如何一步步拿下 Google Offer 的?
  4. 你有程序员朋友吗?告诉他,100 万等他来拿
  5. A 站彻底要凉?近千万条用户数据外泄!
  6. php中throw的作用,php – GOTO和THROW之间的区别?
  7. java对象的内存结构_Java对象在内存中的结构分析
  8. Java定时任务原理
  9. 第 11 章 树结构实际应用
  10. c# 字典按ascii 排序_C语言 按ASCII码排序 求教大神