java内存模型(jMM)(一)
在说java的内存模型之前先简单的了解计算机的主存和缓存的相关概念。
多任务和高并发是衡量一台计算机处理器的重要指标。一般衡量一个服务器性能的高低好坏,使用每秒事务处理数(Transactions Per Second,TPS),它代表着一秒内服务器平均能响应的请求数,而TPS值与程序的并发能力有着非常密切的关系。由于计算机的存储设备与处理器的运算能力之间有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了。但是每个处理器都有自己单独的高速缓存,所以这导致了一个问题:当多个处理器同时对主存中的同一个变量进行操作的时候就会出现缓存不一致以及指令执行顺序错乱问题,而JMM(Java memory model)就是为了解决这些问题。
概念
JMM是一个抽象的概念,并不真实存在。它涵盖了缓存、写缓冲区,寄存器以及其他的硬件和编译器优化。在jvm中,线程共享的变量都存放在堆(Heap)中,每个线程都有其私有的栈空间,私有变量等信息就是存放在栈(Stack)内存中。JMM是一种语言级的内存模型,它确保在不同的编译器和处理器平台上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一直的内存可见性保证
内存交互操作

内存的交互操作定义了工作内存和主内存之前是怎么 进行交互的,java内存模型一共定义一下八种操作:
- lock(锁定):作用于主内存变量,把一个变量标识为一个线程独占状态
- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放的变量才可以再次被其他的线程锁定
- read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中
- load(载入):作用于工作内存的变量,它把read操作从主内存中获取的变量值放入到工作内存的变量副本中
- use(使用):作用于工作内存的变量,把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的字节码指令时将会执行这个操作
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内内存中的变量,每当虚拟机遇到一个给变量赋值的字节码指令时将会执行这个操作
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存,一遍后续的write操作使用
- write(写入):作用 于主内存变量,塔把store操作从工作内存中获取到的值传送到主内存中的变量中
重排序
从java源码到最终实际执行的指令序列,会分别经历下面三种重排序:
- 编译器优化的重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序
- 指令级并行的重排序:现代处理器采用了指令级并行技术(Instruction-Level Parallelism ,ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序
- 内存系统的重排序:由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行
处理器重排序和内存屏障指令
现代处理器使用写缓冲区来临时保存向内存中写入的数据。写缓冲区可以保证指令流水线持续执行,它可以避免由于处理器停顿下来等待向内训中写入数据而产生的延迟。同时,通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,可以减少对内存总线的占用。虽然写缓冲区有这么多的好处,但每个处理器的写缓冲区仅仅对它所在的处理器可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的读/写操作的执行顺序,并不一定于内存实际发生读/写操作顺序一致。所以为了保障内存的可见性,java编译器在生成指令序列的适当位置插入内存屏障指令来禁止特定类型的处理器重排序。JMM吧内存屏障指令分为以下四类:
| 屏障类型 | 指令示例 | 说明 |
| LoadLoad Barriers | Load1; LoadLoad; Load2 | 确保Load1数据的装载,之前于Load2及所有后续装载指令的装载。 |
| StoreStore Barriers | Store1; StoreStore; Store2 | 确保Store1数据对其他处理器可见(刷新到内存),之前于Store2及所有后续存储指令的存储。 |
| LoadStore Barriers | Load1; LoadStore; Store2 | 确保Load1数据装载,之前于Store2及所有后续的存储指令刷新到内存。 |
| StoreLoad Barriers | Store1; StoreLoad; Load2 | 确保Store1数据对其他处理器变得可见(指刷新到内存),之前于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令。 |
StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他三个屏障的效果。现代的大多数处理器都支持这个屏障(其他类型的内存屏障指令不一定所有的处理器都支持)。执行该屏障的开销会很昂贵,因为当前处理器 通常要吧写缓冲区的数据全部刷到内存中去。
顺序一致性
数据竞争与顺序一致性保证
当程序未正确同步时,就可能存在数据竞争。java内存模型对数据竞争的定义如下:
- 在一个线程中写一个变量
- 在另一个线程读同一个变量
- 而且读和写没有通过同步来排序
当代码中包含数据竞争时,程序的执行往往产生违反直觉的结果。如果一个多线程程序能够正确同步,这个程序将是一个没有数据竞争的程序,JMM对正确同步的
程序的内存一致性做了如下保证:
- 如果程序是正确同步的,程序的执行将有顺序一致性(sequentially consistent)--即程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同。这里指的同步是广义上的同步,包括对常用同步原语(synchronized、volatile、final)的正确使用
顺序一致性内存模型
顺序一致性内存模型是一个被计算机科学家理想化了的理论参考模型,它为程序员提供了极强的内存可见性保证。顺序一致性内存模型有量大特性:
- 一个线程中的所有操作必须按照程序的顺序来执行
- (不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须是原子操作且立即对所有线程可见
未同步程序的执行特性
对于未同步或未正确同步的程序,JMM只提供最小的安全性:线程执行读取到的值,要么是之前某个线程写入的值,要么是默认值(0,false,null)。JMM保证读取操作读取到的值不会无中生有(out of thin air),的冒出来。为了实现最小安全性,JMM在堆上分配内存时,首先会清零内存空间,然后才会在上面分配对象(JVM内部会同步这两个操作)。因此,在已清零的内存空间(pre-zeroed memory)分配对象时,域的默认初始化已经完成。
JMM不保证未同步程序的执行结果与该程序在顺序一致性模型中的执行结果一致。因为要想保证执行结果一致,JMM需要禁止大量的处理器和编译器的优化,这对程序的执行性能带来很大的影响。而且未同步程序在顺序一致性模型执行时,整体是无序的,其执行结果往往无法预知。所以保证未同步程序在这两种模型下的执行结果一致没什么 意义。
未同步程序在JMM中执行时,整体上是无序的,其执行结果无法预知。未同步程序在两个模型中的执行特性有一下几个差异:
- 顺序一致性模型保证单线程内的操作按照程序的顺序执行,而JMM不保证单线程内的操作会按程序的顺序执行
- 顺序一致性模型保证所有线程只能看到一致的操作执行顺序,而JMM不保证所有线程能看到一致的操作顺序
- JMM不保证对64位的long/double型变量的读/写操作具有原子性,而顺序一致性模型保证对所有的内存读/写操作都具有原子性
参考资料:<<深入理解java内存模型>> 程晓明著
java内存模型(jMM)(一)的更多相关文章
- Java内存模型JMM与可见性
Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...
- 多线程并发之java内存模型JMM
多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘.其他系统.数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果 ...
- Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)
JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...
- 全面理解Java内存模型(JMM)及volatile关键字(转载)
关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...
- 全面理解Java内存模型(JMM)及volatile关键字(转)
原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...
- 什么是Java内存模型(JMM)
什么是java内存模型 缓存一致性问题 在现代计算机中,因为CPU的运算速度远大于内存的读写速度,因此为了不让CPU在计算的时候因为实时读取内存数据而影响运算速度,CPU会加入一层缓存,在运算之前缓存 ...
- 对多线程java内存模型JMM
多线程概念的引入体现了人类重新有效压力寨计算机.这是非常有必要的,由于所涉及的读数据的过程中的一般操作,如从磁盘.其他系统.数据库等,CPU计算速度和数据读取速度已经严重失衡.假设印刷过程中一个线程将 ...
- 深入理解Java内存模型JMM与volatile关键字
深入理解Java内存模型JMM与volatile关键字 多核并发缓存架构 Java内存模型 Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java线程内存模型是标准化的,屏蔽 ...
- Java内存模型(JMM)详解
在Java JVM系列文章中有朋友问为什么要JVM,Java虚拟机不是已经帮我们处理好了么?同样,学习Java内存模型也有同样的问题,为什么要学习Java内存模型.它们的答案是一致的:能够让我们更好的 ...
- Java并发编程:Java内存模型JMM
简介 Java内存模型英文叫做(Java Memory Model),简称为JMM.Java虚拟机规范试图定义一种Java内存模型来屏蔽掉各种硬件和系统的内存访问差异,实现平台无关性. CPU和缓存一 ...
随机推荐
- file_get_contents无法请求https连接的解决方法 php开启curl
file_get_contents无法请求https连接的解决方法 方法1: PHP.ini默认配置下,用file_get_contents读取https的链接,就会如下错误: Warning: fo ...
- 112. Path Sum二叉树路径和
[抄题]: Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding ...
- 一致性协议之Paxos算法
一.算法提出背景 Paxos算法需要解决的问题就是如何在一个可能发生诸如宕机或网络异常情况的分布式气筒中,快速且正确地在集群内部对某个数据的值达成一致,并且保证不论发生以上任何异常,都不会破坏整个系统 ...
- Qt webkitwidgets模块和webenginewidgets模块
问题 将Qt开发的程序从Qt5.5或更低的版本迁移到5.6或更高的版本时,会提示webkitwidgets是unknown module. Project ERROR: Unknown module( ...
- 最大公约数(gcd)和 最小公倍数(lcm)——辗转相除法
辗转相除法(又称欧几里得算法)是求最大公因数的算法 要求a,b的最大公约数(a>b),我们可以递归地求b,a%b的最大公约数,直到其中一个数变成0,这时另一个数就是a,b的最大公约数. C++实 ...
- jqgrid 单元格放超链接文本
.前台 <%-- builed by manage.aspx.cmt [ver:] at // :: --%> <%@ Page Language="C#" Au ...
- Web测试-day
昨天太忙忘了写博客,今天补上: 这两天完成的工作: 我们组选定了博客园和CSDN作为对比,进行Web测试. 胡俊辉--找到了10个网页的bug,并完成了bug记录文档,并且对CSDN和博客园进行功能分 ...
- 数据库SQL优化大总结之 百万级数据库优化方案(转)
出处:http://www.cnblogs.com/yunfeifei/p/3850440.htm 网上关于SQL优化的教程很多,但是比较杂乱.近日有空整理了一下,写出来跟大家分享一下,其中有错误和不 ...
- IO编程之writelines方法
1.使用with open as 函数写入文件 2.创建后的文件名为database.txt 3.创建一个函数进行读取文件,使用for循环遍历整个文件内容 4.使用if __name__=='__ma ...
- yii2 gridview 新增按钮 动态显示按钮
新增一个按钮 [ 'class' => 'yii\grid\ActionColumn', 'header' => '操作', 'options' => ['width' => ...