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. Linux下常用工具

    GUI篇 计算器gnome-calculator pdf阅读envince 虚拟机virtualbox vnc tigervnc-server and client 网络连接network-manag ...

  2. 测试那些事儿—软测必备的Linux知识(二)

    linux常用命令 用户登录linux后,可以在Linux的命令提示符后面输入命令与系统进行交互. 1.磁盘管理 1.1 cd 切换目录:让登录用户在不同的目录间切换 常用的目录切换 cd~ 进入当前 ...

  3. CH3401 石头游戏(矩阵快速幂加速递推)

    题目链接:传送门 题目: 石头游戏 0x30「数学知识」例题 描述 石头游戏在一个 n 行 m 列 (≤n,m≤) 的网格上进行,每个格子对应一种操作序列,操作序列至多有10种,分别用0~9这10个数 ...

  4. angular6新建项目

    mkdir  angular6project cd angular6project ng new demo      新建一个普通项目 ng new demo --routing  新建一个带路由的项 ...

  5. 第三节《Git重置》

    先来看看.git/refs/heads/master文件的内容 [root@git demo]# cat .git/refs/heads/master e97f443b2d1cee7eeca7dc2e ...

  6. 回顾HashMap

    一.HashMap的原理简述 HashMap是基于哈希表的非线程安全的Map实现,内部采用数组+链表实现,其内部类Node定义了数据元素类型,它扩展了Map.Entry<K,V>增加了指向 ...

  7. 20165308实验三 敏捷开发与XP实践实验报告

    实验三 敏捷开发与XP实践实验报告 实验目的 安装 alibaba 插件,解决代码中的规范问题.再研究一下Code菜单,找出一项让自己感觉最好用的功能. 在码云上把自己的学习搭档加入自己的项目中,确认 ...

  8. rsync的daemon模式

    官方文档:https://download.samba.org/pub/rsync/rsyncd.conf.html   1:daemon模式配置文件         rsync以daemon方式运行 ...

  9. Zookeeper 集群搭建--单机伪分布式集群

    一. zk集群,主从节点,心跳机制(选举模式) 二.Zookeeper集群搭建注意点 1.配置数据文件 myid 1/2/3 对应 server.1/2/3 2.通过./zkCli.sh -serve ...

  10. spingMVC+mybatis+spring-session共享内存配置

    1. redis依赖: <dependency> <groupId>org.springframework.session</groupId> <artifa ...