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. iOS动画学习

    学习一下动画,感谢以下大神的文章:    UIView:基础动画.关键帧动画.转场动画 Core Animation :基础动画,关键帧动画,动画组,转场动画,逐帧动画 CALayer :CALaye ...

  2. jQuery-2.DOM---节点的删除

    DOM节点删除之empty()的基本用法 要移除页面上节点是开发者常见的操作,jQuery提供了几种不同的方法用来处理这个问题,这里我们开仔细了解下empty方法 empty 顾名思义,清空方法,但是 ...

  3. Dubbo的Filter实战--整合Oval校验框架

    前言: 其实很早之前就想写一篇关于oval和具体服务相整合的常见做法, 并以此作为一篇笔记. 趁现在项目中间空闲期, 刚好对dubbo的filter有一些了解. 因此想结合两者, 写一下既结合校验框架 ...

  4. hibernate一级缓存和快照

    摘自网络: Hibernate中的一级缓存的底层是以Map形式存在的,key是主键,value是对象,所以它的泛型为Map<Serializable,Object>,key的泛型为串行化是 ...

  5. C# 利用反射完成计算器可扩展功能

    一个主要的窗体程序,两个输入框,一个label using System; using System.Collections.Generic; using System.ComponentModel; ...

  6. 【java多线程】队列系统之ArrayBlockingQueue源码

    1.简介 ArrayBlockingQueue,顾名思义:基于数组的阻塞队列.数组是要指定长度的,所以使用ArrayBlockingQueue时必须指定长度,也就是它是一个有界队列. 它实现了Bloc ...

  7. 【SpringBoot】SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener

    =================6.SpringBoot拦截器实战和 Servlet3.0自定义Filter.Listener ============ 1.深入SpringBoot2.x过滤器Fi ...

  8. 以Windows服务方式运行ASP.NET Core程序【转载】

    我们对ASP.NET Core的使用已经进行了相当一段时间了,大多数时候,我们的Web程序都是发布到Linux主机上的,当然了,偶尔也有需求要发布到Windows主机上,这样问题就来了,难道直接以控制 ...

  9. crontab定时任务第一个周期未完成下一个周期执行就来了

    一.现象 有一个定时任务,每分钟执行一次,前一分钟还没有执行完成,下一个分钟就来了,怎么解决. 二.模拟 #!/bin/bash echo "开始 `date` " >> ...

  10. 安装Kerberos后,如何不使用它,Current Kerberos password:

    在不知情的情况下,安装了kerberos,然后只有是有密码的地方,一直有这个: Current Kerberos password: 没有了解过kerberos,想要卸载,卸载了还是有,怎么弄都弄不掉 ...