1. 概述

在执行程序时, 为了提高性能, 编译器和处理器常常会对指令做重排序. 为了实现某些功能有时会禁止某些重排序, 由此引入了内存屏障.

2. 重排序

重排序虽然可以提高程序性能, 但是编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序. 即: 编译器和处理器在重排序时, 会遵
守数据依赖性.

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

2-1. as-if-serial语义

as-if-serial语义的意思是: 不管怎么重排序(编译器和处理器为了提高并行度), (单线程)程序的执行结果不能被改变. 编译器、runtime和处理器都必须遵守as-if-serial语义.

为了遵守as-if-serial语义, 编译器和处理器不会对存在数据依赖关系的操作做重排序, 因为这种重排序会改变执行结果. 但是, 如果操作之间不存在数据依赖关系, 这些操作就可能被编译器和处理器重排序.

2-2. 重排序的种类

  1. 编译器优化的重排序: 编译器在不改变单线程程序语义的前提下, 可以重新安排语句的执行顺序.
  2. 指令级并行的重排序: 现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行. 如果不存在数据依赖性, 处理器可以改变语句对应机器指令的执行顺序.
  3. 内存系统的重排序: 由于处理器使用缓存和读/写缓冲区, 这使得加载和存储操作看上去可能是在乱序执行.

2-3. 从Java源代码到最终实际执行的指令序列, 会分别经历下面3中重排序.

源代码 -> 1:编译器优化重排序 -> 2:指令级并行重排序 -> 3:内存系统重排序 -> 最终执行的指令序列

其中1属于编译器重排序, 2和3属于处理器重排序. 这些重排序可能会导致多线程程序出现内存可见性问题. 对于编译器, JMM编译器重排序规则会禁止特性类型的编译器重排序(并不是所有的编译器重排序都要禁止); 对于处理器重排序, JMM的处理器重排序规则会要求Java编译器在生成指令序列时, 插入特性类型的内存屏障(Memory Barriers, Intel称之为Memory Fence)指令, 通过内存屏障指令来禁止特定类型的处理器重排序.

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

3. 内存屏障类型

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

写缓冲区仅对自己的处理器可见, 它会导致处理器执行内存操作的顺序可能会与内存实际的操作执行顺序不一致. 由于处理器都会使用写缓冲区, 因此现代处理器都会允许对写-读操作进行重排序.

3-1. 处理器的重排序规则

可以发现常见的处理器都允许StoreLoad重排序; 常见的处理器都不允许对存在数据依赖的操作做重排序. SPARC-TSO和X86拥有相对较强的处理器内存模型, 它们仅允许对写-读操作做重排序(因为它们都使用了写缓冲区).

3-2. 内存屏障类型表

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

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

4. 总结

重排序可以提高性能, 但是重排序可能会导致内存可见性问题, 问了解决这个问题, 编译器在生成字节码的时候会插入特定类型的内存屏障来禁止重排序, 保证多线程下的内存可见性.

JMM中的重排序及内存屏障的更多相关文章

  1. J.U.C JMM. pipeline.指令重排序,happen-before(续)

    前面已经介绍硬件平台Cache Coherence问题和解决办法,下面来看看Java虚拟机平台的相关知识.硬件平台处理器,高速缓存,主存之间的交互关系如下: Java内存模型(JMM)         ...

  2. J.U.C JMM. pipeline.指令重排序,happen-before

    pipeline: 现在的CPU一般采用流水线方式来执行指令.一个指令执行周期被分成:取值,译码,执行,访存,写会,更新PC若干阶段.然后,多条指令可以同时存在于流水线中,同时被执行,来提高系统的吞吐 ...

  3. Jvm 中的 重排序、主存、原子操作

    一.重排序 好处:重排序可以提升性能,避免在一个耗时很长的指令在“执行”阶段呆很长时间,而导致后续的指令都卡在“执行”之前的阶段上. 坏处:重排序对多线程的影响 class ReorderExampl ...

  4. JS中数组重排序方法

    在数组中有两个可以用来直接排序的方法,分别是reverse()和sort().下面通过本文给大家详细介绍,对js数组重排序相关知识感兴趣的朋友一起看看吧 1.数组中已存在两个可直接用来重排序的方法:r ...

  5. Javascript中数组重排序方法详解

    在数组中有两个可以用来直接排序的方法,分别是reverse()和sort().下面通过本文给大家详细介绍,对js 数组重排序相关知识感兴趣的朋友一起看看吧. 1.数组中已存在两个可直接用来重排序的方法 ...

  6. J.U.C JMM. pipeline.指令重排序,happen-before(续MESI协议)

    缓存(Cache)       CPU的读/写(以及取指令)单元正常情况下甚至都不能直接访问内存——这是物理结构决定的:CPU都没有管脚直接连到内存.相反,CPU和一级缓存(L1 Cache)通讯,而 ...

  7. Java内存访问重排序笔记

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

  8. JVM内存模型、指令重排、内存屏障概念解析

    在高并发模型中,无是面对物理机SMP系统模型,还是面对像JVM的虚拟机多线程并发内存模型,指令重排(编译器.运行时)和内存屏障都是非常重要的概念,因此,搞清楚这些概念和原理很重要.否则,你很难搞清楚哪 ...

  9. JVM内存模型、指令重排、内存屏障概念解析(转载)

    在高并发模型中,无是面对物理机SMP系统模型,还是面对像JVM的虚拟机多线程并发内存模型,指令重排(编译器.运行时)和内存屏障都是非常重要的概念,因此,搞清楚这些概念和原理很重要.否则,你很难搞清楚哪 ...

随机推荐

  1. IntelliJ IDEA 如何生成时序图?

    进入扩展程序安装 File > Settings > Plugins > Browse Repositories 搜索 SequenceDiagram,点击右边 Install 安装 ...

  2. 13--Python入门--文件读写--CSV&Excel文件

    EXCEL文件 import pandas as pd excel=pd.read_excel('read_excel.xlsx') print(excel) CSV文件 import pandas ...

  3. python 实现快速排序和插入排序

    def quick_sort(data): if len(data)<2 or (len(data)==2 and data[0]<=data[1]): #[2,3]此情况会死循环 所以加 ...

  4. linux日常命令之三

    一.换行符 linux换行符为\n,而windows换行符为\r\n. 因此,linux的原生文本文件,换行符为\n,而windows为\r\n:将linux文件拷贝至windows,换行符保持不变, ...

  5. 作用域&&闭包

    在了解闭包之前,先了解作用域一,作用域简单来说就是变量和函数可以访问的范围,在es5中变量作用域一般分为全局作用域和局部作用域,这个主要依据是全局变量还是局部变量 情景1: <script> ...

  6. BIOS + MBR > UEFI + GPT

    BIOS + MBR > UEFI + GPT硬件接口系统与磁盘分区UEFI用于取代老旧的BIOS,而GPT则取代老旧的MBR. 名词解释: BIOS (Basic Input/Output S ...

  7. 深入学习Motan系列(三)——服务发布

    袋鼠回头看了看文章,有些啰嗦,争取语音简练,不断提高表达力!袋鼠奋起直追! 注:此篇文章,暂时为了以后时间线排序的需要,暂时发表出来,可是仍然有许多地方需要改写.自己打算把服务端发布,客户端订阅都搞定 ...

  8. 第三章 JQuery: HelloWorld--常见方法--css--选择器--筛选器--属性--效果--事件--数组操作--字符串操作--对象转换

    1.jQuery简介 为了简化JavaScript 的开发, 一些JavsScript 库诞生了. JavaScript库封装了很多预定义的对象和实用函数.能帮助使用者建立有高难度交互的页面, 并且兼 ...

  9. 第一章 HTML+CSS(中)

    4.域元素(form表单.textarea文本域.fieldset域集合.input使用) 案例 表单 用户名: 密码: 昵称: 你喜欢的水果有? 苹果 黄瓜 香蕉 请选择性别 男 女 请选择你要的网 ...

  10. django无法同步mysql数据库 Error:1064

    [问题] 具体问题:新建django工程,使用django的manage.py的 migrate命令进行更改. 在初始化数据库表时,失败,错误信息为 django.db.migrations.exce ...