Java内存模型深度解析:总结

处理器内存模型

顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照。JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序一致性模型来实现处理器和JMM,那么很多的处理器和编译器优化都要被禁止,这对执行性能将会有很大的影响。

根据对不同类型读/写操作组合的执行顺序的放松,可以把常见处理器的内存模型划分为下面几种类型:

  1. 放松程序中写-读操作的顺序,由此产生了total store ordering内存模型(简称为TSO)。
  2. 在前面1的基础上,继续放松程序中写-写操作的顺序,由此产生了partial store order 内存模型(简称为PSO)。
  3. 在前面1和2的基础上,继续放松程序中读-写和读-读操作的顺序,由此产生了relaxed memory order内存模型(简称为RMO)和PowerPC内存模型。

注意,这里处理器对读/写操作的放松,是以两个操作之间不存在数据依赖性为前提的(因为处理器要遵守as-if-serial语义,处理器不会对存在数据依赖性的两个内存操作做重排序)。

下面的表格展示了常见处理器内存模型的细节特征:

内存模型名称 对应的处理器

Store-Load 重排序 Store-Store重排序 Load-Load 和Load-Store重排序 可以更早读取到其它处理器的写 可以更早读取到当前处理器的写
TSO sparc-TSOX64 Y       Y
PSO sparc-PSO Y Y     Y
RMO ia64 Y Y Y   Y
PowerPC PowerPC Y Y Y Y Y

在这个表格中,我们可以看到所有处理器内存模型都允许写-读重排序,原因在第一章以说明过:它们都使用了写缓存区,写缓存区可能导致写-读操作重排序。同时,我们可以看到这些处理器内存模型都允许更早读到当前处理器的写,原因同样是因为写缓存区:由于写缓存区仅对当前处理器可见,这个特性导致当前处理器可以比其他处理器先看到临时保存在自己的写缓存区中的写。

上面表格中的各种处理器内存模型,从上到下,模型由强变弱。越是追求性能的处理器,内存模型设计的会越弱。因为这些处理器希望内存模型对它们的束缚越少越好,这样它们就可以做尽可能多的优化来提高性能。

由于常见的处理器内存模型比JMM要弱,java编译器在生成字节码时,会在执行指令序列的适当位置插入内存屏障来限制处理器的重排序。同时,由于各种处理器内存模型的强弱并不相同,为了在不同的处理器平台向程序员展示一个一致的内存模型,JMM在不同的处理器中需要插入的内存屏障的数量和种类也不相同。下图展示了JMM在不同处理器内存模型中需要插入的内存屏障的示意图:

如上图所示,JMM屏蔽了不同处理器内存模型的差异,它在不同的处理器平台之上为java程序员呈现了一个一致的内存模型。

JMM,处理器内存模型与顺序一致性内存模型之间的关系

JMM是一个语言级的内存模型,处理器内存模型是硬件级的内存模型,顺序一致性内存模型是一个理论参考模型。下面是语言内存模型,处理器内存模型和顺序一致性内存模型的强弱对比示意图:

从上图我们可以看出:常见的4种处理器内存模型比常用的3中语言内存模型要弱,处理器内存模型和语言内存模型都比顺序一致性内存模型要弱。同处理器内存模型一样,越是追求执行性能的语言,内存模型设计的会越弱。

JMM的设计

从JMM设计者的角度来说,在设计JMM时,需要考虑两个关键因素:

  • 程序员对内存模型的使用。程序员希望内存模型易于理解,易于编程。程序员希望基于一个强内存模型来编写代码。
  • 编译器和处理器对内存模型的实现。编译器和处理器希望内存模型对它们的束缚越少越好,这样它们就可以做尽可能多的优化来提高性能。编译器和处理器希望实现一个弱内存模型。

由于这两个因素互相矛盾,所以JSR-133专家组在设计JMM时的核心目标就是找到一个好的平衡点:一方面要为程序员提供足够强的内存可见性保证;另一方面,对编译器和处理器的限制要尽可能的放松。下面让我们看看JSR-133是如何实现这一目标的。

为了具体说明,请看前面提到过的计算圆面积的示例代码:

double pi  = 3.14;    //A
double r   = 1.0;     //B
double area = pi * r * r; //C

上面计算圆的面积的示例代码存在三个happens- before关系:

  1. A happens- before B;
  2. B happens- before C;
  3. A happens- before C;

由于A happens- before B,happens- before的定义会要求:A操作执行的结果要对B可见,且A操作的执行顺序排在B操作之前。 但是从程序语义的角度来说,对A和B做重排序即不会改变程序的执行结果,也还能提高程序的执行性能(允许这种重排序减少了对编译器和处理器优化的束缚)。也就是说,上面这3个happens- before关系中,虽然2和3是必需要的,但1是不必要的。因此,JMM把happens- before要求禁止的重排序分为了下面两类:

  • 会改变程序执行结果的重排序。
  • 不会改变程序执行结果的重排序。

JMM对这两种不同性质的重排序,采取了不同的策略:

  • 对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序。
  • 对于不会改变程序执行结果的重排序,JMM对编译器和处理器不作要求(JMM允许这种重排序)。

下面是JMM的设计示意图:

从上图可以看出两点:

  • JMM向程序员提供的happens- before规则能满足程序员的需求。JMM的happens- before规则不但简单易懂,而且也向程序员提供了足够强的内存可见性保证(有些内存可见性保证其实并不一定真实存在,比如上面的A happens- before B)。
  • JMM对编译器和处理器的束缚已经尽可能的少。从上面的分析我们可以看出,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。比如,如果编译器经过细致的分析后,认定一个锁只会被单个线程访问,那么这个锁可以被消除。再比如,如果编译器经过细致的分析后,认定一个volatile变量仅仅只会被单个线程访问,那么编译器可以把这个volatile变量当作一个普通变量来对待。这些优化既不会改变程序的执行结果,又能提高程序的执行效率。

JMM的内存可见性保证

Java程序的内存可见性保证按程序类型可以分为下列三类:

  1. 单线程程序。单线程程序不会出现内存可见性问题。编译器,runtime和处理器会共同确保单线程程序的执行结果与该程序在顺序一致性模型中的执行结果相同。
  2. 正确同步的多线程程序。正确同步的多线程程序的执行将具有顺序一致性(程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同)。这是JMM关注的重点,JMM通过限制编译器和处理器的重排序来为程序员提供内存可见性保证。
  3. 未同步/未正确同步的多线程程序。JMM为它们提供了最小安全性保障:线程执行时读取到的值,要么是之前某个线程写入的值,要么是默认值(0,null,false)。

下图展示了这三类程序在JMM中与在顺序一致性内存模型中的执行结果的异同:

只要多线程程序是正确同步的,JMM保证该程序在任意的处理器平台上的执行结果,与该程序在顺序一致性内存模型中的执行结果一致。

JSR-133对旧内存模型的修补

JSR-133对JDK5之前的旧内存模型的修补主要有两个:

  • 增强volatile的内存语义。旧内存模型允许volatile变量与普通变量重排序。JSR-133严格限制volatile变量与普通变量的重排序,使volatile的写-读和锁的释放-获取具有相同的内存语义。
  • 增强final的内存语义。在旧内存模型中,多次读取同一个final变量的值可能会不相同。为此,JSR-133为final增加了两个重排序规则。现在,final具有了初始化安全性。

转载于:https://www.cnblogs.com/dengshiwei/p/4258458.html

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

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

相关文章

sourcetree,创建工作流报错:Fatal: Not a gitflow-enabled repo yet. Please run 'git flow init' first.-》解决办法...

1、打开项目下.git/config文件,或者如下图操作: 2、打开config文件以后,删除所有 [gitflow *条目并保存文件 3、关闭并重新打开sourcetree 4、仓库-》Git 工作流-》初始化仓库即可转载于:https://www.cnblogs.com/yxfeng/p/10536955.html

关于a标签的href属性的注意事项

今天在做一个lightbox效果的时候出现了一个问题。 当往下滚动再点击按钮出现lightbox的时候,lightbox的遮罩层不能铺满(即滚动高度处不能铺上),如下图所示。原因是提交按钮使用的是a标签,当给a标签写上href属性的时候&…

在mac上安装gitlab

参考链接: https://www.cnblogs.com/floodwater/p/10138265.html 注意事项: 在安装gitlab-ce时,配置hostname域名后,通过域名访问gitlab时,需要配置本机hosts文件,不然不能访问 本地hosts文件中配置后 就可…

d3.js 入门指南

说到数据可视化,我们会行到很多优秀的框架,像echarts、highcharts,这些框架很优雅,健壮,能满足我们对可视化的大部分需求,但是缺点也很明显,就是这些框架几乎是不可定制化的,当遇到特…

Kubernetes系列之Helm介绍篇

本次系列使用的所需部署包版本都使用的目前最新的或最新稳定版,安装包地址请到公众号内回复【K8s实战】获取 介绍 Helm 是 Deis 开发的一个用于 Kubernetes 应用的包管理工具,主要用来管理 Charts。有点类似于 Ubuntu 中的 APT 或 CentOS 中的 YUM。Helm…

HTNL笔记整合

简述概括了HTML 的部分内容,不是很完善,希望能给予你们相对的帮助。 一下文件的整合百度云链接:HTML整合笔记 第一章 HTML入门 课时1:HTML初识 1、英文名(Hyper Text Markup Language)超文本标签语言 对…

EXCEL 图表 只在拐点的时候显示数字

EXCEL图表只在折线的拐点显示数值,中间不需要显示。同时往下拐的,显示在上方,往上的显示在下方,这样数值不会挡住线。 首先,做一些模拟数据 因为起点和终点数值必须显示,所以单元格,C2 D2 C19 D…

SQL语法(1、安装操作)

1、数据库的系统概述及安装与基本使用 bilibili可查找安装视频百度了解一下 – 使用超级管理员登录 CONN sys/change_on_install AS SYSDBA ; – 创建c##scott用户 CREATE USER c##scott IDENTIFIED BY tiger ; – 为用户授权 GRANT CONNECT,RESOURCE,UNLIMITED TABLESPACE…

第22章:MongoDB-聚合操作--聚合管道--$out

①$out$out:利用此操作可以将查询结果输出到指定的集合里面。②范例:将投影的结果输出到集合里③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳转载于:https://www.cnblogs.com/Lucky-stars/p/10555296.html

SQL简单查询

1、简单查询 使用Oracle sql developer使用前,必须开启的服务: 查询emp表上的数据: select * from emp; Null为空,空不代表等于没有,null!0. 重新连接后,注意大小写及空格位! 简…

SQL限定查询

1、限定查询与排序显示 1.1限定查询的认识: 列:表中有大数据的信息,对数据进行筛选,查询到自己想要的信息。 (数据过多显示过慢,或者死机,在已有的样本数据库容器CDB转换为PDB之中)…

浅谈五大Python Web框架

http://www.csdn.net/article/2011-02-17/292058 导读:作者飞龙写了一篇《浅谈Python Web框架》,文中他介绍了几个Python Web框架和自己对选择框架的分析。在他看来,用Django来快速开发一些Web运用是很不错的选择。以下是文章内容&#xff1a…

主流浏览器和内核及Web标准

目前网络市场的浏览器主流: 课时3:web标准 WEB标准 w3c 万维网联盟组织,制定web标准的机构。 网页主要由三部分组成: 结构(Structure)、 表现(Presentation) 行为(Beh…

机器学习中的损失函数 (着重比较:hinge loss vs softmax loss)

https://blog.csdn.net/u010976453/article/details/78488279 1. 损失函数 损失函数(Loss function)是用来估量你模型的预测值 f(x)f(x) 与真实值 YY 的不一致程度,它是一个非负实值函数,通常用 L(Y,f(x))L(Y,f(x)) 来表示。损失函…

HTML入门第一和第二章

课时4:HTML初识 1、英文名(Hyper Text Markup Language)超文本标签语言 对网页上的内容进行描述 课时5:HTML骨架 课时6:我的第一个页面及其标签简介 课时7:骨架记忆法 课时8:什么是标签及其分…

H5第一天

移动Web - 基础&流式布局 目标 了解移动端主要浏览器的内核掌握用谷歌浏览器调试移动端页面(重要)了解布局视口、视觉视口、理想视口使用mate标签设置理想视口(重要)了解视网膜屏、物理像素、二倍图会使用background-size设…

boost::timer demo

#include <iostream> #include <boost/timer.hpp> //timer的头文件 using namespace boost; //打开boost名字空间int main(int argc, char** argv) {timer t; //定义一个计时器对象,并开始计时/*可度量的最大时间,以小时为单位*/std::cout << "max …

H5的第二天

移动web开发——flex布局 目标 了解flex布局的优缺点及原理能够说出flex布局原理、使用语法、特点&#xff08;重点&#xff09;能够使用flex布局常用属性&#xff08;重点&#xff09;能够独立完成携程移动端首页 1.0 传统布局和flex布局对比 1.1传统布局 兼容性好布局繁…

d3.js 入门指南 - 仪表盘

D3的全称是Data-Driven Documents&#xff08;数据驱动的文档&#xff09;&#xff0c;是一个用来做数据可视化的JavaScript函数库&#xff0c;而JavaScript文件的后缀通常为.js&#xff0c;所以D3被称为D3.js。 d3.js可以定制出各种图形&#xff0c;今天来用d3.js制作一个简易…

[转帖]华为的“大海思”与“小海思”

华为的“大海思”与“小海思” https://www.cnbeta.com/articles/tech/828275.htm没先到华为海思这么狠.. 作为华为的全资子公司&#xff0c;说起海思半导体&#xff0c;大家可能第一时间会想起麒麟处理器。经过多年的持续的研发投入&#xff0c;华为海思自研的麒麟处理器现在确…