JVM级别内存屏障如何禁止指令重排序的

news2024/11/24 4:38:03

承接上文证明CPU指令是乱序执行的

当多个cpu访问同一份数据的时候怎么保证数据的一致性?

在最底层级别的控制有好多种:

第一种叫关中断,就是访问任何数据的时候必须有一个中断信号量的存在。很多传统的cpu就是靠它实现的,从内存读东西的时候实际上是通过中断响应去读的,比如访问这块内存的时候把中断给关了,任何读也好,写也好,io操作中断全不响应,就没有人能打断你,因为把能打断我的所有的指令都给关了,只要没人打断你,就一定能保证数据的一致性,这也涉及到芯片中断的内容;

第二种就是CPU缓存一致性;

第三种就是系统屏障:cpu访问内存数据,把数据线锁住,只允许一颗cpu传数据,其他cpu就不能传,同一时刻只有一个cpu可以访问这个数据,

怎么防止指令重排序?

第一种情况是禁止编译器乱序,有的代码在编译的过程中就直接乱序了;

第二种是使用内存屏障阻止指令乱序执行。

随便写2个指令,在编译器编译的时候就可能会产生乱序,只要前后没有依赖关系就有可能产生,这是在编译阶段;有前后关联关系的是不能够随便换顺序的,比如x=1和y=x+1;没有关联关系的话,就有可能换顺序,比如x=1和y=2。

c语言的volatile,底层是通过这条指令禁止编译器将前后2个指令换顺序执行的。

a=1和b=d这两个指令在编译器编译的时候不能换顺序执行,这种被称为内存屏障。

volatile是指令级别的屏障 ,它也是一种特殊的指令;

不同的cpu,内存屏障的指令是不一样的,因特尔的内存屏障有lfence(读屏障)、sfence(写屏障)、mfence(不管读还是写都不能越过)。

内存屏障指令有很多种,不同的cpu是不同的指令,使用内存屏障来阻止指令的乱序执行。

除了lfence、mfence、sfence这些内存屏障指令之外的其他有哪些内存屏障?

在jvm中也存在内存屏障,jvm不是一台实体的机器,不像intel一样具备一个物理的cpu,jvm只是一个逻辑概念,jvm内存屏障一共规定了4类,所有实现jvm规范的虚拟机必须实现四个屏障:

读读指令中间加一个LoadLoad(LL屏障),读读就不能换顺序;

写写指令中间加一个SS屏障,写写就不能换顺序;

读写指令中间加一个LS屏障,读写指令不能换顺序。

规定了相邻的2个操作不能换顺序,就相当于一个屏障。

在cpu的基础之上构建了操作系统os来管理cpu;

jvm在os系统看来只是一个很普通的程序而已,jvm在自己的程序里规定了好多屏障,最终这些屏障的实现也得靠cpu和os提供的能力。

不能将jvm级别的内存屏障和系统级别的内存屏障混在一起。

jvm是c++写的,在里面规定了自己写的内存屏障,jvm想实现内存屏障最终还得映射成cpu的内存屏障。

volatile的实现细节

volatile两大作用:

第一个是保障可见性:一个cpu改了的内容另外一个cpu马上可见;

第二个作用是禁止指令重排序:比如new对象时候的三个指令不会换顺序执行。

volatile其实是一个普通的关键字,无非是修饰了某块内存和某块变量。

在jvm层做一个特殊的操作,jvm规定volatile所修饰的变量,对这块内存做写操作的时候,在它前面必须加一个屏障SS,后面加个屏障叫SL,前面的所有写操作必须先执行完,然后再往里面写,必须等我写完,别人才能读。

必须等我读完别人才能读,必须等我读完别人才能写。

volatile修饰的内存,对它的任何访问全都换不了顺序,

这个只是jvm自己规定的,最终一定要体现在cpu级别的,cpu级别怎么实现的?

不同的jvm有不同的实现,最流行的jvm hotspot oracle所提供的,hotspot volatile底层到底怎么实现的?

java写的volatile,jvm编译执行,java是解释执行的,所以要想了解volatile怎么实现的,得去读hotspot解释器的代码,看是怎么解释完成的?

二进制码解释器的实现类是bytecodeInterpreter.cpp,可以看到是怎么解释运行volatile的,

可以看到最终的实现是一条汇编指令,不是lfence 、mfence、sfence。

两条指令可以乱序执行,多线程的情况会读到中间状态的各种各样的情形,所以必须得实现一种机制,不让两条指令乱序执行,最底层cpu级别实现了指令级别的机制,编译器级别也实现了禁止编译器优化的指令,jvm级别也实现了自己的逻辑操作,但是jvm级别的指令最终要落到cpu级别,cpu级别最终是怎么落上去的?

is_MP方法是判断是否为多个处理器(或多个cpu),如果是的话就执行lock addl,l是lense的意思,rsp或esp是寄存器指令 ,把某个寄存器的值加上一个0,为什么lock指令可以实现禁止指令重排序?

每种cpu最终的对于屏障的实现应该对应特定的指令,比如lfense、mfense指令,但是hotspot偷了个懒,cpu通过执行lock指令来实现:当cpu或线程访问某个内存的时候,会锁住总线,不允许其他cpu去读或去写,必须等我读完写完,其他才可以继续,这样就不会乱序了。

lock主要用于在多处理器中执行指令时对共享内存的独占使用,将当前处理器对应的缓存刷新到内存并使其他处理器对应的缓存失效,其他处理器得重新读,另外还提供了禁止指令重排序即无法越过内存屏障的作用。

凡是在lock前后加任何指令都不能越过,因为它是一个全屏障。

对某个寄存器加个0,这个操作跟没有是一样的,没有任何作用,简单称之为空指令,

为什么有一个空指令存在,因lock指令在锁总线的时候,这条指令后面必须跟一条指令,后面指令不能为空,所以后面得跟一条指令,但是后面跟的又不能有任何作用,如果有任何作用,中间改了别的值,也不对,所以设计了这么一个指令,往某个寄存器上加了个0,跟没有操作一样,主要为了迎合lock指令的参数要求即后面必须跟一个指令,其实只要有一个lock指令就足够了。

虽然lock后面加了个空操作,但是lock起着锁总线的作用。

因特尔cpu设计的禁止重排序指令是mfense和lfense,其实也没有想到hotspot会有lock这样的操作,作为hotspot来讲,不同的cpu应该做对应的优化,不应该为了偷懒就直接使用了一条lock指令。

lock其实是很多底层的实现,比如synchronized本身也是用lock来实现的,volatile也是。

那lock到底是什么?

lock并不能简单的认为是锁住总线,想了解lock指令就相当于你要了解cpu级别的并发控制到底有哪些种,cpu级别的对于多线程的内存并发控制:

第一个叫关中断;

第二种是缓存一致性协议;

第三种是系统屏障,系统屏障本身第一个级别是编译级别的屏障,第二个级别是指令级别的屏障;

第四种是总线和缓存锁lock cmpxchg memory或lock addl,这条指令要么缓存锁,要么总线锁,所以它未必一定是总线锁,想在cpu级别控制整个并发,只有这四种 。

在操作系统在这四种最基本的操作之上会提供一系列的内核级别的api,让你调用api去实现各种各样的锁。

各种各样的锁包括哪些东西?

从linux内核的角度大概是包括这些内容:信号量和P-V原语(也就是+-的操作),还有一个是互斥。

在这些个api的基础之上,还有互斥量MUTEX、自旋锁CAS。

在自旋锁的基础上还会有读写锁、中断控制、内核抢占,SEQ锁、序列锁、RCU锁。

在多cpu访问下必须要考虑访问同一个数据会出现数据不一致的问题,cpu级别提供了4种控制的方式 ,在这4种控制方式的基础之上,不同的操作系统提供了一系列内核级别的api,在api的基础之上提供了一系列的锁来做并发控制。

在这些内核基础之上,才完成了jvm级别的锁控制,jvm级别除了原来的synchronized之外,还有juc级别的锁,比如cas级别的automic开头的原子类的操作。

在juc里面,在所有底层的基础之上,才会诞生了java这一系列的锁以及自己实现锁所需要的最原始的零件。

要了解jvm锁,建议从底层开始了解,所有上层的东西就是对最底层的一个封装而已。

jvm封装了pthread和kthread,这种是linux内核级别的api,它封装了cpu级别的4种方式,提供的一系列的同步机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/365767.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高速风筒的IPM模块解决方案

高速吹风筒是利用高转速产生的大风量来快速吹干头发,同时,高转速也使得电机与叶轮的体积缩小,便于设计出灵巧便携的外形。12万转的高速风筒的整体解决方案,满足高速吹风筒的所有应用场景,让客户用芯能的功率器件能更快…

PMP认证的PDU是什么?

PDU(Professional Development Units)即专业发展单元,是指您获取认证后,就项目管理专业进行学习、授课、或提供志愿服务的累积时间,以小时为单位,1小时即累积1个PDU。自证书获取日起,以三年为A周…

Ansible 多机自动化工具 初学笔记

此文档仅张聪明同学个人笔记;新负责KTH-RPL Cluster GPU的漫漫学习长路 English Docs: official https://docs.ansible.com/ansible/latest/index.html 中文相关文档: https://ansible.leops.cn/basic/Quickstart/https://blog.csdn.net/xinshuzhan/a…

java基础学习 day44(多态的优点和劣势)

1. 多态的优势 在多态形式下,右边对象可以实现解耦合(即之后的代码与右边的子类对象不绑定,在更改子类对象后,之后的代码仍可以使用),便于扩展和维护在定义方法的时候,使用父类型作为参数&…

北京/东莞/广州/深圳2023年上半年软考(中/高级)报名>>>

软考是全国计算机技术与软件专业技术资格(水平)考试(简称软考)项目,是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试,既属于国家职业资格考试,又是职称资格考试。 系统集成…

TCP/IP体系结构、Socket、进程PID、端口Port

计算机网络小结 一、TCP/IP体系结构 首先OSI参考模型,OSI将网络分为七层,自下而上分别是物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。 TCP/IP体系结构则将网络分为四层,自下而上分别是网络接口层、网络层、传输层、应用…

(二十五)、实现评论功能(5)【uniapp+uinicloud多用户社区博客实战项目(完整开发文档-从零到完整项目)】

1,实现二级回复的入库操作 1.1 两个子组件(comment-item和comment-frame)与父组件reply之间的属性传值 comment-item: props: {item: {type: Object,default () {return {}}}},comment-frame: props: {commentObj: {…

LoRaWAN模块在车辆跟踪定位中的应用

目前 GPS已经在资产的管理中得到了越来越多的运用,如车辆跟踪、车队跟踪、资产监控等;人员跟踪,宠物跟踪,等等。在所有追踪装置中,最重要的是它的电池期望和监视距离。鉴于 LoRaWAN的功率消耗很小,而且能在…

Allegro如何快速检查走线是否跨分割操作指导

Allegro如何快速检查走线是否跨分割操作指导 在做PCB设计的时候,信号线跨分割的情况需要尽量避免,尤其是信号速率较高的情况,Allegro可以快速的检查PCB上哪个位置跨分割了,并且以高亮的形式报出来,类似下图 TOP层走线跨了L2层参考层空洞, 如何检查,具体操作步骤如下 点…

ArkUI实战,自定义饼状图组件PieChart

本节笔者带领读者实现一个饼状图 PieChart 组件,该组件是根据笔者之前封装的 MiniCanvas 实现的, PieChart 的最终演示效果如下图所示: 饼状图实现的拆分 根据上图的样式效果,实现一个饼状图,实质就是绘制一个个的实…

如何保护 IP 地址的隐私问题

是不是只有运营商才能查到某个人的住址信息呢?在大数据时代的今天,各种互联网应用收集了大量的数据信息,它们其实也可以根据这些信息,推断出某个人的大致地址位置。例如百度地图会一直用 App SDK 以及网页的方式记录 IP 和地址位置…

MySQL-redo log和undo log

什么是事务 事务是由数据库中一系列的访问和更新组成的逻辑执行单元 事务的逻辑单元中可以是一条SQL语句,也可以是一段SQL逻辑,这段逻辑要么全部执行成功,要么全部执行失败 举个最常见的例子,你早上出去买早餐,支付…

位运算(C/C++)

1. 基础知识 程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二…

Python概述 基础语法 判断 循环

Python概述常用快捷键第二章-Python基础语法01-字面量02-注释03-变量04-数据类型05-数据类型转换06-标识符07-运算符08-字符串的三种定义方式09-字符串的拼接10-字符串格式化11-字符串格式化的精度控制 12-字符串格式化-快速写法13-对表达式进行格式化14-字符串格式化练习题讲解…

【Jmeter】ForEach控制器

一、什么是ForEach控制器 ForEach控制器是遍历某个数组读取不同的变量值,来控制其下的采样器或控制器执行一次或多次。而这个数组可以是用户自定义变量,也可以是从前面接口请求中提取到需要的数据,然后进行遍历循环。 二、ForEach控制器相关…

【概念辨析】二维数组传参的几种可能性

一、二维数组传参竟然不是用二级指针进行接收? 今天进行再一次的二级指针学习时,发现了一条以前没怎么注意过的知识点:二维数组进行传参只能用二维数组(不能省略列)进行接收或者是数组指针。 问题复现代码如下&#xf…

深度卷积对抗神经网络 进阶 第三部分 GANs Unpaired Translation with Cycle GAN 模型

非配对的图像转换应用 Unpaired Image-to-Image Translation Unpaired image-to-image translation 主要用于学习两组图像之间的对应关系,检查和寻找两堆数据中的共同内容(content)以及每堆独有的特点(style)。而这个…

FinClip 的 2022 与 2023

相比往年,今年复盘去年与展望新年的文章来的稍慢一点。不过也希望能够借这篇文章,和关注 FinClip 的用户朋友们一起聊聊,我们在去年和今年的想法与计划。 2022 在过去的一年中,我们的身边发生了很多事情,这些事情在不…

英语二-电子邮件邀请短文写作

1. 邮件模板 Dear 邀请人, Hope you have a great day. I am writing this email to invite you to attend 主题. Please kindly find the following information for your reference: Time: 时间 Address: 地点 We hope that nothing will prevent you from coming, as…

如何轻松学习Python数据分析?

今天这篇文章来聊聊如何轻松学习『Python数据分析』,我会以一个数据分析师的角度去聊聊做数据分析到底有没有必要学习编程、学习Python,如果有必要,又该如何学习才能做到毫不费力。 1.实际的工作 如果你是一名数据分析师,我相信你…