一、Java内存模型

硬件处理

电脑硬件,我们知道有用于计算的cpu、辅助运算的内存、以及硬盘还有进行数据传输的数据总线。在程序执行中很多都是内存计算,cpu为了更快的进行计算会有高速缓存,最后同步至主内存,大概的交互如下图

为了使处理器内部的运算单元能够被充分的利用,处理器可能会对输入代码进行乱序执行优化,然后将计算后的结果进行重组,保证该结果和顺序执行的结果是一致的(单位时间内,一个core只能执行一个线程,所以结果的一致仅限一个线程内)。

Java内存模型

Java内存模型是语言级别的模型,它的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取数变量这样的底层细节。

在内存里,java内存模型规定了所有的变量都存储在主内存(物理内存)中,每条线程还有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行。不同的线程无法访问别线程的工作内存里的内容。下图展示了逻辑上 线程、主内存、工作内存的三者交互关系。

内存交互操作

Java内存模型定义的8个操作指令来进行内存之间的交互

read 读取主内存的值,并传输至工作内存load 将read的变量值存放到工作内存

read 好比快递运输车,工作内存好比站点,运输车将快递运输到站点,站点必须得卸货 load。

use 将工作内存的变量值,传递给执行引擎assign 执行引擎对变量进行赋值

use好比站点进行快递员分配,站点说我把快递分给你了快递员A。快递员A接收到快递assign开始派送。

store 工作内存将变量传输到主内存write 主内存将工作内存传递过来的变量进行存储

store和 write就好理解了,快递员A将快递送到你家门口(store),然后你得签收(write)

lock 用作主内存变量,它把一个变量在内存里标识为一个线程独占状态unlock 用作主内存变量,它对被锁定的变量进行解锁

下图展示下工作内存和主内存间的指令操作交互

指令规则

read 和 load、store和write必须成对出现assign操作,工作内存变量改变后必须刷回主内存同一时间只能运行一个线程对变量进行lock,当前线程lock可重入,unlock次数必须等于lock的次数,该变量才能解锁对一个变量lock后,会清空该线程工作内存变量的值,重新执行load或者assign操作初始化工作内存中变量的值。unlock前,必须将变量同步到主内存(store/write操作)二、重排序

从java源码到最终实际执行的指令序列,会经历下面3种重排序

从源码到最终执行的指令序列的示意图

重排序的现象

a=1,b=a 这一组 b依赖a,不会重排序;

a=1,b=2 这一组 a和b没有关系,那么就有可能被重排序执行 b=2,a=1

编译器优化的重排序编译器在不改变单线程程序语义的前提下,可以重新安排语句执行顺序指令级并行的重排序现代处理器采用了指令级并行技术将多条指令重叠执行。在不存在数据依赖的时候,处理器可以改变指令执行顺序内存系统重排序处理器使用高速缓存,使得多运算单元加载和存储主内存操作看上去可能在乱序执行重排序的代码示例,文章底部的参考文章里有示例,这里就不罗列了。

三、内存屏障

JMM(java 内存模型) 在不改变程序执行结果的前提下,尽可能的支持处理器的重排序。通过禁止特定特定类型的编译器重排序和处理器重排序,为开发者提供一致的内存可见性保证,如 volatile、final。

Java编译器在生成指令的时候会在适当位置插入内存屏障来进制特定类型的处理器排序。

内存屏障说的通俗一点就是一个栏杆,在两个指令之间插入栏杆,后面的指令就不能越过栏杆先执行。

JMM定义的内存屏障指令分为4类

LoadLoad指令示例 Load1LoadLoadLoad2确保Load1数据装载一定先于Load2及后续所有Load指令LoadStore指令示例 Load1LoadStoreStore2确保Load1数据装载一定先于Store2及后续所有Store指令StoreStore指令示例 Store1StoreStoreStore2确保Store1主内存落地(从工作内存刷入主存,其它线程可见)一定先于Store2及后续所有Store指令StoreLoad指令示例 Store1StoreLoadLoad2确保Store1主内存落地(从工作内存刷入主存,其它线程可见)一定先于Load2及后续所有Load指令处理器对重排序的支持

从上面可以看到不同的处理器架构对重排序的支持也是不一样(其它处理器架构暂不罗列),所以不同的平台JMM的内存屏障施加也略有不同,具体来说,比如 X86 对Load1Load2不支持重排序,那么你就没有必要施加 LoadLoad屏障。

四、volatile的内存语义

volatile我们都知道是java的关键字用来保证数据可见性,防止指令重排的效果。包括JUC里AQS Lock的底层实现也是基于volatitle来实现。

volatile写的内存语义

当写一个volatile变量的时候,JMM会把该线程对应的本地内存变量值刷新到主内存

volatile读的内存语义

当读一个volatile变量的时候,JMM会把线程本次内存置为无效。线程接下来将从主内存中读取共享变量(也就是重新从主内存获取值,更新运行内存中的本地变量)

上面两个语义,保证了volatile变量写入对线程的可见性

volatile内存屏障插入规则

volatile内存屏障策略

代码简单示例

1 class X { 2 int a, b; 3 volatile int v, u; 4 5 void f() { 6 int i, j; 7 8 i = a;// load a 普通load 9 j = b;// load b 普通load10 i = v;// load v volatile load11 // LoadLoad12 j = u;// load u volatile load13 // LoadStore14 a = i;// store a 普通store 15 b = j;// store b 普通store16 // StoreStore17 v = i;// store v volatile store18 // StoreStore19 u = j;// store u volatile store20 // StoreLoad21 i = u;// load u volatile load22 // 两个屏障 LoadLoad 和 LoadStore23 j = b;// load b 普通load24 a = i;// store a 普通store25 }26 }上述代码可以套用volatile屏障规则对应。

当然不同的处理器架构重排序的支持也是不一样,比如X86 只有当 store1 load2 的时候会进行重排序,那么就会省略掉很多类型的内存屏障。

五、final的内存语义

final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。

被final修饰的变量不能被修改,方法不能被重写,类不能被继承。

我们暂时把final修饰的称作域,对于final域,编译器和处理器要遵守两个重排序规则

写规则

在构造函数内对一个final域的写入,与随后把这个被构造的对象的引用赋值给一个引用变量,这两个操作不可重排序JMM禁止编译器把final域的写重排序到构造函数之外

编译器会在final域写入的后面插入StoreStore屏障,禁止处理器把final域的写重排序到构造函数之外。

该规则可以保证在对象引用为任意线程可见之前,对象的final域已经被正确初始化,而普通域无法保障。

读规则

初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。在一个线程中,初次读对象引用与初次读该对象包含的final域,JMM禁止处理器重排序这两个操作。编译器会在读final域操作的前面插入一个LoadLoad屏障。

该规则保证在读一个对象的final域之前,一定会先读包含这个域的对象引用。

关注 + 转发后,私信关键词 “ Java ”

可获取一份Java架构进阶技术精品视频。(高并发+Spring源码+JVM原理解析+分布式架构+微服务架构+多线程并发原理+BATJ面试宝典)

重要的事情说三遍,转发、转发、转发后再发私信,才可以拿到!

java内存模型 final_聊聊Java内存模型相关推荐

  1. jvm内存结构_聊聊JVM内存结构

    起因 我们经常会在面试的时候被问到JVM的内存结构,很多人会觉得这东西真的有用吗?也就是面试造火箭,入职拧螺丝.问这个就是纯粹来刁难人的吧. 但实际上,我们细想一下. •假设你不知道局部变量实际上属于 ...

  2. java序列化_今天聊聊 Java 序列化

    点击上方 Java后端,选择 设为星标 优质文章,及时送达在开发过程中经常会对实体进行序列化,但其实我们只是在"只知其然,不知其所以然"的状态,很多时候会有这些问题: 什么是序列化 ...

  3. java 获取泛型_聊聊Java泛型擦除那些事

    >版权申明]非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89789849 出自:shushen ...

  4. java解决异常_聊聊Java中的异常及处理

    在编程中异常报错是不可避免的.特别是在学习某个语言初期,看到异常报错就抓耳挠腮,常常开玩笑说编程1分钟,改bug1小时.今天就让我们来看看什么是异常和怎么合理的处理异常吧! 异常与error介绍 下面 ...

  5. java final 内存_聊聊 Java 内存模型

    原标题:聊聊 Java 内存模型 *作者:青芒@有赞 本文目录 Java内存模型 重排序 内存屏障 volatitle的内存语义 final的内存语义 一.Java内存模型 硬件处理 电脑硬件,我们知 ...

  6. 图文并茂的聊聊Java内存模型!

    在面试中,面试官经常喜欢问:『说说什么是Java内存模型(JMM)?』 面试者内心狂喜,这题刚背过:『Java内存主要分为五大块:堆.方法区.虚拟机栈.本地方法栈.PC寄存器,balabala--』 ...

  7. Java 理论与实践: 修复 Java 内存模型,第 2 部分 (VOLATILE, FINA...

    为什么80%的码农都做不了架构师?>>>    活跃了将近三年的 JSR 133,近期发布了关于如何修复 Java 内存模型(Java Memory Model, JMM)的公开建议 ...

  8. 【深入Java虚拟机JVM 03】Java内存模型

    说明:文章所有内容均摘自<深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)> Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的"高墙",墙外面的 ...

  9. java内存图怎么画,jvm内存模型怎么画?简单美观的模型图制作软件

    jvm内存模型是什么呢?jvm内存模型是一类用于描述由java栈.java堆.方法区.本地方法栈和程序计数器各部件构成的Java程序.在执行过程中,由jvm管理的不同数据区域的一类图表.jvm内存模型 ...

最新文章

  1. 微信小程序多张图片和表单一起上传,验证表单及进度条的实现完整代码
  2. 卷积神经网络(CNN)综述
  3. C#统计字符出现的个数【C#】
  4. 2017年单多晶市场竞争核心分析
  5. 泛型 java 总结_JAVA泛型总结
  6. 敏捷、TDD(测试驱动开发)、OO--前奏
  7. 著名程序员 Eric S. Raymond :用 SaaS 是一种危险的愚蠢行为
  8. Async/await 和 Promises 区别
  9. 在Eclipse中手动安装pydev插件
  10. Windows核心编程_在Visual Studio窗口输出调试信息
  11. Atitit 单项功能开发 最佳实践规范 标准化流程attilax总结.docx
  12. CPU检测软件CPU-Z的下载使用
  13. ps中基色 混合色 结果色是什么
  14. 基于OpenHarmony的智慧牧场
  15. 不逼自己一把,命运就会逼你一辈子
  16. 难过!能不能放下抢票套路,我只想买好好回家过年!我在网上抢火车票:多加了100元的加速包,却依然买不到票...
  17. 自组织(竞争型)神经网络
  18. 在opencv2.4.x版本中处理图像EXIF orientation不统一的问题
  19. 苏州企业申报科技项目的小技巧汇总
  20. 从前车马很慢,书信很远,一生只够爱一个人

热门文章

  1. linux下打开关闭端口命令
  2. 从前我只知世事疾苦,没曾想,你是彩蛋,而且还是最甜的那一个。
  3. 【分治+斜率优化】BZOJ2149拆迁队 CF660F Bear and Bowling 4
  4. Javascript 获取class
  5. python全栈工程师完整版视频教程
  6. Python 全栈路线图(转载参考)
  7. 三级网络技术(七.路由器)
  8. android课程设计 备忘录
  9. 生物特征识别技术在宠物监控中的应用
  10. 苹果电脑安装win11双系统