JMM(Java Memory Model java内存模型

2024-04-15 18:12
文章标签 java 内存 模型 model memory jmm

本文主要是介绍JMM(Java Memory Model java内存模型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目标:

搞清楚高并发场景下,java内存模型是怎么支持的,对象在内存中是怎么布局的?

 

目录

目标:

搞清楚高并发场景下,java内存模型是怎么支持的,对象在内存中是怎么布局的?

1.硬件层的并发优化基础知识

 2.多核CPU线程访问条件下数据不一致性问题?

 原因:

 解决方案:

缓存行

MESI带来的伪共享问题

3.CPU的乱序执行问题

 CPU乱序执行的原因

CPU乱序执行的表现

读指令的乱序执行

写指令的合并执行(write combining)

如何保证特定情况下,不乱序

CPU级别采用内存屏障保证或者Java的汇编指令 Lock :

JVM级别再进行规范

Java代码级别采用volatitle关键字保障

1.字节码层面(.class文件)

2.JVM层面

3. os和硬件层面


1.硬件层的并发优化基础知识

备注:

L3高速缓存也是在主板上,所以也是被所有的CPU共享的。

L0寄存器,存储着CPU内部核心的几个计算单元,用于计算。

离CPU越近容量越小,成本越高,速度越快。

比如说 有个变量int a=3;在 L2上,计算时CPU会先把a  load到L1上,然后被L0直接加载计算。

 2.多核CPU线程访问条件下数据不一致性问题?

 原因:

两个CPU彼此独立,但是都是操作CPU的共享区域的数据,比如一个CPU 对X作了修改,另一CPU是不能及时知道的。这就造成了共享区域的数据对各个不同的CPU 来说数据不一致问题。

 解决方案:

老的CPU:

总线锁会锁住总线,使得其他CPU甚至不能访问内存中其他的地址,因而效率较低

 新的CPU

现在CPU的数据一致性,通过缓存锁+总线锁来解决

https://www.cnblogs.com/z00377750/p/9180644.html

新的CPU采用缓存锁(缓存一致性)来解决,一致性的协议又很多,Intel采用了MESI协议。

 CPU会对缓存的内容做状态标记,如果读取的内容相对于主存来说被更改过(自己修改过),则状态改这个内容对应的缓存行状态改为Modified(别的cpu对这个内容所在缓存行状态就是Invalid);如果读取的内容自己独享,就将其所在缓存行状态改为Exclusive。如果读取的内容,我读的时候别人也在读取,该内容所在缓存行状态就修改为Shared。如果读取的内容别的cPU已经修改过,说明我的读的内容无效了,该内容所在缓存行  状态就变为Invalid。

缓存行

现在cpu 缓存数据,不是一个字节一个字节来缓存,而是以一个缓存行,默认64字节。一次性加载到缓存中。

MESI带来的伪共享问题

如果自己要修改某个数据A,发现它的状态已经变为了Invalid,则去缓存中再加载一次。

思考这个场景: x,y位于同一个缓存行,cpu1只想的读取x,cpu2只想读取y
由于读取时必须以缓存行为单位,cpu1 读取x时,也读取到了y。 cpu1改变了x的值后,x所在的缓存行的状态在CPU1中就会被改变为modified, 在cpu2中x所在的缓存行状态就会变为invalid。 因此,cpu2 本来想读取y,一看y 所在缓存行状态变为了 invalid 。就会去重新加载y所在的缓存行。问题是cpu2其实只关注y,y的值也没有变化,而x的变化和它无关。因此本质上cpu2不需要去进行这次缓存加载的。
同理cpu2对y作了修改,后cpu1也得重新加载x所对应的缓存行。
这样的重新加载是一种时间和资源的浪费,缓存一致性或者说缓存锁解决数据安全问题的的同时带来的 这个多次非必要加载缓存问题被称为伪共享问题。

3.CPU的乱序执行问题

首先明确一个事实,CPU执行指令的时候是乱序执行的。

 CPU乱序执行的原因

https://www.cnblogs.com/liushaodong/p/4777308.html

CPU的执行指令在内存中做一些操作,因为CPU的执行速度相较于内存至少时100倍,所以CPU不能干等着执行结果,为了提高效率,CPU就 在一条指令执行结束之前,继续执行别的指令。这种不按照程序的编写顺序执行的情况,被称为CPU的乱序执行问题。乱序变现在读指令的乱序,和写指令的乱序。

当然乱序执行不能带来程序的最终执行结果的乱序。所以,计算机底层,对CPU的乱序执行也是作了规范,也保障乱序执行的结果是正确的。提高了执行效率,也保证执行结果的正确性。

CPU乱序执行的表现

读指令的乱序执行

CPU在执行一条读指令后等待该指令执行完(或者返回结果)前,回去执行另一条与该指令没有依赖关系的读指令

写指令的合并执行(write combining)

合并写:

CPU将计算好的值A缓存到L1,如果缓存失败,就会缓存到L2,再此过程中L2相对于CPU太慢了,如果这个过程中A值被改写了几次。CPU会直接将改写的最终结果刷到L2。这种情况被称为合并写

如何保证特定情况下,不乱序

某些特定场景写要保证顺序,如何保障有序性

CPU级别采用内存屏障保证或者Java的汇编指令 Lock :

sfence:在sfence指令前的写操作当必须在sfence指令后的写操作前完成。
lfence:在lfence指令前的读操作当必须在lfence指令后的读操作前完成。
mfence:在mfence指令前的读写操作当必须在mfence指令后的读写操
作前完成。
原子指令,如x86上的”lock …” 指令是一个FullBarrier,执行时会锁住内存子系统来确保执行顺
序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持
程序顺序

JVM级别再进行规范

LoadLoad屏障: 对于这样的语句Load1; LoadLoad; Load2,
在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

StoreStore屏障:
对于这样的语句Store1; StoreStore; Store2,
在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

LoadStore屏障:
对于这样的语句Load1; LoadStore; Store2,
在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

StoreLoad屏障: 对于这样的语句Store1; StoreLoad; Load2,
​ 在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

 备注:这些规范的实现,是依赖于硬件级别的原理来实现的。具体用哪些硬件原理,怎么组合操作,JVM自己决定。

Java代码级别采用volatitle关键字保障

1.字节码层面(.class文件)

被volatile 修饰的变量在.class二进制字节码文件中多了一个acc_volatile 标记

2.JVM层面

JVM对被acc_volatile  修饰的变量,也即 .class文件中被 acc_volatile修饰的变量:

        如果它是写操作指令,则在该写操作前面加一个 StoreStoreBarrier ,后面加一个StoreLoadBarrier ,保证该指令与上下两个指令不重排序

        如果它是读操作指令,则在该读操作前面加一个 LoadLoadBarrier ,后面加一个LoadStoreBarrier ,保证该指令与上下两个指令不重排序

3. os和硬件层面

windows 上采用Lock 指令和MESI 实现,Linux我忘了,哈哈哈

这篇关于JMM(Java Memory Model java内存模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/906552

相关文章

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与