Java内存模型JMM
java内存模型定义
上一遍文章我们讲到了CPU缓存一致性以及内存屏障问题。那么Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬件差异,给上层的开发者一个一致的使用接口。Java内存模型就是这样一个中间层的模型,它为程序员屏蔽了底层的硬件实现细节,支持大部分的主流硬件平台。
java内存模型(Java Memory Mode):java内存模型是java虚拟机内存如何与计算机内存(RAM)一起工作。java虚拟机是是整个计算机的模型,所以这个模型自然包含一个内存模型。也可以说JMM是java虚拟机内存使用规范。
通俗的来讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。
Java内存模型规定了不同线程如何以及何时可以看到其他线程写入共享变量的值以及如何在必要时同步对共享变量的访问。
注意:Java Memory Model并不是真实存在的,他只是物理内存模型的一个映射。
Java 内存模型介绍
JVM中内存分配的两个概念:
stack(栈)
特点: 存取速度快、对象生命周期确定、数据大小确定。
存储数据:基本类型变量、对象引用(句柄)
位置:缓存、寄存器、写缓冲区。heap(堆)
特点: 存取速度慢、运行时动态分配大小、对象生命抽周期不确定、垃圾回收。
存储数据:对象
位置:主内存、缓存
理论上说所有的stack和heap都存储在物理主内存中,但随着CPU运算其数据的副本可能被缓存或者寄存器持有,持有的数据遵从一致性协议.
存储方式
上面说到,一个对象时存储在heap上的,对象中所属的方法与方法的成员变量存储在stack上。一个对象的成员变量随着对象本身存储在堆上,无论该对象类型是引用类型或者是基本类型。 静态变量和对象类定义存储于堆上。
并发原因
存储在堆上的对象可以被持有该对象引用的栈访问。能访问对象,也就能访问该对象中的成员变量。当了两个线程同时访问一个对象时,每个线程都拥有该对象成员变量的私有拷贝。
这里只是粗略分配了java内存模型。具体细节的内存分配请查看
JVM内存管理概述
Java内存模型与系统内存模型
我们来看看一个关系图:
在系统内存架构中并没有栈(stack)、堆(heap)这种概念,只有寄存器(register)、缓存(cache)、主内存(RAM、Main Memory)。理论上说所有的栈和堆都存储在主内存中,但随着CPU运算其数据的副本可能被缓存或者寄存器持有。持有的数据遵从CPU-Cache一致性协议。
CPU内存模型、一致性协议可以参考前一篇文章死磕并发之CPU缓存一致性协议(MESI)
Java 内存模型抽象结构图
主内存:保存了所有的变量。
共享变量:如果一个变量被多个线程使用,那么这个变量会在每个线程的工作内存中保有一个副本,这种变量就是共享变量。
比如成员变量、静态变量、数组元素等。
工作内存:每个线程都有自己的工作内存,线程独享,保存了线程用到了变量的副本(主内存共享变量的一份拷贝)。工作内存负责与线程交互,也负责与主内存交互。为了更高的效率java虚拟机、硬件系统可能让工作内优先分配在寄存器、缓存中。
JMM对共享内存的操作做出了如下两条规定:
- 线程对共享内存的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写。
- 不同线程无法直接访问其他线程工作内存中的变量,因此共享变量的值传递需要通过主内存完成。
java并发问题的根源
假设线程A和线程B同事访问某个对象的成员变量x。当线程a需要操作变量a,时会将a副本复制到线程A的工作内存中。
当线程a未执行完毕,线程b也要访问变量a
但是线程a与线程b操作的是自己工作空间中的变量副本。 线程a中的副本和线程b中间的副本相符不可见。如果a线程率先完成了任务并写回主存。那么线程b的运算就是在使用后脏数据运算。如果b也写回主存那么线程a的任务就会丢失。
为了保证程序的准确性,我们就需要在并发时添加额外的同步操作。
java内存模型-内存间的八种同步操作
操作过程
我们接着再来关注下变量从主内存读取到工作内存,然后同步回工作内存的细节,这就是主内存与工作内存之间的交互协议。Java内存模型定义了以下8种操作来完成,它们都是原子操作(除了对long和double类型的变量)。
锁定(lock):作用于主内存中的变量,将他标记为一个线程独享变量。
通常意义上的上锁,就是一个线程正在使用时,其他线程必须等待该线程任务完成才能继续执行自己的任务。
解锁(unlock):作用于主内存中的变量,解除变量的锁定状态,被解除锁定状态的变量才能被其他线程锁定。
执行完成后解开锁。
read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
从主内存 读取到工作内存中。
load(载入):把read操作从主内存中得到的变量值放入工作内存的变量的副本中。
给工作内存中的副本赋值。
use(使用):把工作内存中的一个变量的值传给执行引擎,每当虚拟机遇到一个使用到变量的指令时都会使用该指令。
程序执行过程中读取该值时调用。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
将运算完成后的新值赋回给工作内存中的变量,相当于修改工作内存中的变量。
store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
将该值从变量中取出,写入工作内存中。
write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。
将工作内存中的值写回主内存。
读取执行步骤
写入执行步骤
操作规则
不允许read和load、store和write操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者从工作内存发起回写了但主内存不接受的情况出现。
不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。
不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中。
一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。
如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定住的变量。
回顾
1.Java内存模型是一个规范,他规定了不同线程如何以及何时可以看到其他线程写入共享变量的值以及如何在必要时同步对共享变量的访问。
2.java内存模型要求,调用栈和本地变量存储在线程栈上,对象存放在堆上。线程之间的通信必须要经过主内存。
3.定义了同步的八个操作,以及使用这八个操作需要遵守的规则。
并发研究之Java内存模型(Java Memory Model)的更多相关文章
-
Java内存模型(Java Memory Model,JMM)
今天简单聊聊什么叫做 Java 内存模型,不是 JVM 内存结构哦. JMM 是一个语言级别的内存模型,处理器的硬件模型是硬件级别,Java中的内存模型是内存可见性的基本保证.从而为我们 volati ...
-
Java 内存模型- Java Memory Model
多线程越来越多的使用,使得我们需要对它的深入理解.那么就涉及到了Java内存模型JMM.JMM是JVM的一部分,JMM定义了一个线程修改了一个共享变量,其他线程什么时候或者如何看到这个变量,如何去访问 ...
-
【深入理解JAVA虚拟机】第5部分.高效并发.1.Java内存模型与线程。
1.概述 摩尔定律:描述处理器晶体管数量与运行效率之间的发展关系.Amdahl定律:通过系统中并行化与串行化的比重来描述多处理器系统能获得的运算加速能力. 从摩尔定律到Amdahl定律的转变,代表了近 ...
-
并发编程之Java内存模型
在介绍Java内存模型之前,先来了解一下为什么要有内存模型,以及内存模型是什么.然后我们基于对内存模型的了解,学习Java内存模型以及并发编程的三大特性. 为什么要有内存模型 在计算机中,所有的运算操 ...
-
java高并发系列 - 第7天:volatile与Java内存模型
public class Demo09 { public static boolean flag = true; public static class T1 extends Thread { pub ...
-
Java 内存模型都不会,就敢在简历上写熟悉并发编程吗
从 PC 内存架构到 Java 内存模型 你知道 Java 内存模型 JMM 吗?那你知道它的三大特性吗? Java 是如何解决指令重排问题的? 既然CPU有缓存一致性协议(MESI),为什么 JMM ...
-
Java并发编程:JMM (Java内存模型) 以及与volatile关键字详解
目录 计算机系统的一致性 Java内存模型 内存模型的3个重要特征 原子性 可见性 有序性 指令重排序 volatile关键字 保证可见性和防止指令重排 不能保证原子性 计算机系统的一致性 在现代计算 ...
-
高效并发一 Java内存模型与Java线程(绝对干货)
高效并发一 Java内存模型与Java线程 本篇文章,首先了解虚拟机Java 内存模型的结构及操作,然后讲解原子性,可见性,有序性在 Java 内存模型中的体现,最后介绍先行发生原则的规则和使用. 在 ...
-
硬件内存模型到 Java 内存模型,这些硬核知识你知多少?
Java 内存模型跟上一篇 JVM 内存结构很像,我经常会把他们搞混,但其实它们不是一回事,而且相差还很大的,希望你没它们搞混,特别是在面试的时候,搞混了的话就会答非所问,影响你的面试成绩,当然也许你 ...
-
Java虚拟机12:Java内存模型
什么是Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的访问差异,以实现让Java程序在各种平台下都能达到一致 ...
随机推荐
-
ADO.NET学习系列(四)---窗体版的登录小程序
1.需求分析:做一个登录的小程序,基于Winform的窗体小程序.基本要求:登录成功:弹框显示登录成功,登录失败就弹框显示失败. 扩展功能:登录次数超过3次,就”锁定“用户,提示登录错误次数过多,不能 ...
-
寻虫记:BOM头制造的冤案,无故多出空白行
最近在做的一个网站发生了一个很诡异的BUG: 使用IE浏览页面时,一切都挺正常: 而使用Firefox浏览时,发现某些页面元素之间的距离比预期的要宽很多,HTML元素本身的hight.padding和 ...
-
Codeforces Round #130 (Div. 2) A. Dubstep
题目链接: http://codeforces.com/problemset/problem/208/A A. Dubstep time limit per test:2 secondsmemory ...
-
【PHPsocket编程专题(实战篇③)】构建基于socket的HTTP请求类
该代码是两年前写的,现在看起来有点渣了,仅仅是提供一个思路,现在做一些Api开发的时候官方会有一些SDK,这些SDK其实原理都是通过socket来通讯的,其实我个人主张用curl更方便,当然前提是你的 ...
-
Debian 命令行方式配置网络
一.对于有线网络,如果默认没有安装图形界面,进入了 multi-user.target中时,是没有使用NetworkManager管理网络的,此时需要手动配置才能上网 首先得到网卡名称:ip addr ...
-
java生成pdf文件 --- Table
Java利用itext实现导出PDF文件 所需要的jar包:com.lowagie.text_2.1.7.v201004222200.jar jar包下载地址:http://cn.jarfire.or ...
-
cocos JS 定时器
cocos2d-js的定时器的创建跟使用: 情况一: var TestLayer = cc.Layer.extend({ sprite:null, ctor:function () { this.sc ...
-
灵活控制 Hibernate 的日志或 SQL 输出,以便于诊断
我们在使用 Hibernate 时一般只会关注是否显示生成的 SQL 语句,不过有些时候还不够.默认时 Hibernate 执行的 SQL 语句是打印在控制台上的,它也可以配置为输出给 Log4J 或 ...
-
Linux基础六(网络管理)
目录 一.网络配置 1. IP 地址配置 2. 网络配置文件 3. 虚拟机网络配置参数 二.网络命令 1. 网络环境查看命令 2. 网络测试命令 三.远程会话安全协议-SSH原理 1. SSH 原理 ...
-
[转载]C#委托与事件--简单笔记
原文地址:https://www.cnblogs.com/joeymary/p/8486358.html 委托 简单记录点东西 适合似懂非懂的朋友看看委托类型用来定义和响应应用程序中的回调.借此可以设 ...