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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input

Spring Gateway动态路由实现方案

《SpringGateway动态路由实现方案》本文主要介绍了SpringGateway动态路由实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前沿何为路由RouteDefinitionRouteLocator工作流程动态路由实现尾巴前沿S