Java内存屏障详解

 更新时间:2025年01月08日 09:55:44   作者:夜夜流光相皎洁_小宁  
文章主要介绍了内存屏障的必要性,以及在多核处理器中如何通过内存屏障来保证多线程程序的内存可见性和防止指令乱序执行,文章还详细解释了内存屏障的作用、原理以及常见处理器中的重排序类型

为什么要有内存屏障

为了解决cpu,高速缓存,主内存带来的的指令之间的可见性和重序性问题。

我们都知道计算机运算任务需要CPU和内存相互配合共同完成,其中CPU负责逻辑计算,内存负责数据存储。CPU要与内存进行交互,如读取运算数据、存储运算结果等。由于内存和CPU的计算速度有几个数量级的差距,为了提高CPU的利用率,现代处理器结构都加入了一层读写速度尽可能接近CPU运算速度的高速缓存来作为内存与CPU之间的缓冲:将运算需要使用

的数据复制到缓存中,让CPU运算可以快速进行,计算结束后再将计算结果从缓存同步到主内存中,这样处理器就无须等待缓慢的内存读写了。

什么是内存屏障

内存屏障,也称内存栅栏,内存栅障,屏障指令等, 是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。

程序编译优化、cache访问优化、多核等导致CPU指令乱序执行,最终程序运行不符合我们预期。内存屏障会设置一个同步点,保障屏障前后的多核内存访问数据的一致性。

问题的由来

造成乱序访问的原因分为两类:

一类是主动的,编译器会主动重排代码使得特定的cpu执行更快,称之为编译乱序。

另外一类是被动的,为了异步化指令的执行,引入Store Buffer和Invalidate Queue,却导致了指令顺序改变的副作用。

1)指令重排序

上述的1属于编译器重排序,2和3属于处理器重排序。这些重排序都可能会导致多线程程序出现内存可见性问题。

2)store buffer

加入了这个硬件结构后,CPU0需要往某个地址中写入一个数据时,它不需要去关心其他的CPU的local cache中有没有这个地址的数据,它只需要把它需要写的值直接存放到store buffer中,然后发出invalidate的信号,等到成功invalidate其他CPU中该地址的数据后,再把CPU0存放在store buffer中的数据推到CPU0的local cache中。每一个CPU core都拥有自己私有的store buffer,一个CPU只能访问自己私有的那个store buffer。

该硬件同时也有缺陷,每个CPU的store buffer不能实现地太大,其存储队列的数目也不会太多。当CPU以中等的频率执行store操作的时候(假设所有的store操作都导致了cache miss),store buffer会很快的呗填满。在这种情况下,CPU只能又进入阻塞状态,直到cacheline完成invalidation和ack的交互后,可以将store buffer的entry写入cacheline,从而让新的store让出空间之后,CPU才可以继续被执行。

3)Invalidate Queues

store buffer之所以很容易被填满,主要是因为其他CPU在回应invalidate acknowledge比较慢,如果能加快这个过程,让store buffer中的内容尽快写入到cacheline,那么就不会那么容易被填满了。

CPU其实不需要完成invalidate就可以回送acknowledgement消息,这样就不会阻止发送invalidate的那个CPU进去阻塞状态。

CPU可以将这些接收到的invalidate message存放到invalidate queues中,然后直接回应acknowledge,表示自己已经收到请求,随后会慢慢处理,当时前提是必须在发送invalidate message的CPU发送任何关于某变量对应cacheline的操作到bus之前完成。

4)乱序处理器

类比工业流水线,一条指令的执行可以分拆为多步:获取、解码、运算和结果的写入,每个步骤由一个特定的功能模块执行,如此拆分的好处是多条执行变串行执行为并行执行

5)什么场景下需要使用内存屏障

在两个线程之间存在需要通过共享内存来实现交互的可能时,才需要使用内存屏障,,保证共享变量的可见性。

内存屏障指令

处理器重排序类型

下面是常见处理器允许的重排序类型的列表:

处理器Load-LoadLoad-StoreStore-StoreStore-Load数据依赖
spare-TSONNNYN
X86NNNYN
ia64YYYYN
PowerPCYYYYN

上表单元格中的“N”表示处理器不允许两个操作重排序,“Y”表示允许重排序。

从上表我们可以看出:

1)常见的处理器都允许Store-Load重排序;

2)常见的处理器都不允许对存在数据依赖的操作做重排序。sparc-TSO和x86拥有相对较强的处理器内存模型,它们仅允许对写-读操作做重排序(因为它们都使用了写缓冲区)。

JVM内存屏障指令分类

屏障类型指令示例说明
LoadLoadBarriersLoad1;LoadLoad;Load2;确保Load1的数据加载,前于Load2及所有后续装载指令的装着
StoreStoreBarriersStore1;StoreStore;Store2;确保Store1数据对其他处理器可见(刷新到内存),前于Store2及所有后续存储指令的存储。
LoadStoreBarriersLoad1;LoadStore;Store2;确保Load1数据装载,前于Store2及所有后续的存储指令刷新到内存。
StoreLoadBarriersStore1;StoreLoad;Load2;确保Store1数据对其他处理器变得可见(指刷新到内存),之前于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令。

StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他三个屏障的效果。

现代的多处理器大都支持该屏障(其他类型的屏障不一定被所有处理器支持)。

执行该屏障开销会很昂贵,因为当前处理器通常要把写缓冲区中的数据全部刷新到内存中(buffer fully flush)。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java如何实现图片的叠加与拼接操作

    Java如何实现图片的叠加与拼接操作

    这篇文章主要介绍了Java如何实现图片的叠加与拼接操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • springboot使用注解实现鉴权功能

    springboot使用注解实现鉴权功能

    这篇文章主要介绍了springboot使用注解实现鉴权功能,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-12-12
  • Java项目中“zip END header not found“错误的解决方案

    Java项目中“zip END header not found“错误的解决方案

    在 Java 项目构建或运行过程中,开发者常会遇到 java.util.zip.ZipException: zip END header not found 错误,这一异常通常与 JAR 文件损坏、下载不完整、编码问题或 Maven 依赖管理配置不当有关,本文给大家介绍了Java项目中“zip END header not found“错误的解决方案
    2025-06-06
  • js判断是否是移动设备登陆网页的简单方法

    js判断是否是移动设备登陆网页的简单方法

    这篇文章主要介绍了js判断是否是移动设备登陆网页的简单方法,需要的朋友可以参考下
    2014-02-02
  • Java中的JVM内存分析与故障排查指南

    Java中的JVM内存分析与故障排查指南

    Java虚拟机(JVM)是Java应用的运行时环境,其内存管理机制直接影响着应用的性能和稳定性,本文将介绍JVM内存分析的基本方法,重点介绍如何使用jmap、jhat和VisualVM等工具进行内存分析,并探讨常见的内存泄漏排查方法,需要的朋友可以参考下
    2025-11-11
  • Java求解两个非负整数最大公约数算法【循环法与递归法】

    Java求解两个非负整数最大公约数算法【循环法与递归法】

    这篇文章主要介绍了Java求解两个非负整数最大公约数算法,结合实例形式分析了java求解最大公约数的实现方法,并附带了循环法与递归法算法思路,需要的朋友可以参考下
    2018-03-03
  • Java Synchronized的偏向锁详细分析

    Java Synchronized的偏向锁详细分析

    synchronized作为Java程序员最常用同步工具,很多人却对它的用法和实现原理一知半解,以至于还有不少人认为synchronized是重量级锁,性能较差,尽量少用。但不可否认的是synchronized依然是并发首选工具,本文就来详细讲讲
    2023-04-04
  • Java使用二分法进行查找和排序的示例

    Java使用二分法进行查找和排序的示例

    这篇文章主要介绍了Java使用二分法进行查找和排序的示例,二分插入排序和二分查找是基础的算法,需要的朋友可以参考下
    2016-04-04
  • 详解Java设计模式之单例模式

    详解Java设计模式之单例模式

    单例模式是一种创建型设计模式,它的目的是确保一个类只有一个实例,并提供一个全局访问点来访问该实例,在单例模式中,类自身负责创建自己的唯一实例,并确保在系统中只有一个实例存在,本文详细介绍了Java设计模式中的单例模式,感兴趣的同学可以参考阅读
    2023-05-05
  • MyBatis 配置复用从入门到精通

    MyBatis 配置复用从入门到精通

    本文将深入探讨MyBatis的各种配置复用技巧,帮助您写出更优雅、更易维护的持久层代码,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2026-03-03

最新评论