java内存模型详细分析

      java虚拟机规范中试图定义一种内存模型来屏蔽掉各种硬件和操作系统内存访问差异,以实现让java程序员在各种平台下都能达到一致的内存访问效果。在此之前主流程序语言如c/c++直接使用物理硬件和操作系统的内存模型,因此会由于不同平台上内存模型的差异,有可能导致程序在一套平台上并发完全正常,而在另外一套平台上会出错,因此在某些场景就必须针对不同的平台来编写程序。定义java内存模型并非一件容易的事情,这个模型必须定义的足够严谨,才能让并发访问内存不会产生歧义,但是必须定义的足够宽松,使得虚拟机的实现由足够的自由空间去应用硬件的各种特性如寄存器,高速缓存,和指令集中某些特有的指令来获取更好的执行速度,经过长时间的验证和修补在jdk1.5发布后java内存模型就已经成熟和完善了

1,主内存和工作内存

  java内存模型的主要目的是定义程序中各个变量的访问规则,及就是虚拟机将变量存储到内存和从内存中取出变量的这样底层细节。它包括了实例字段,静态字段和构成属虚兑现的元素,但不包括局部变量与方法参数,因为后者是线程私有的,不会被共享(这里说明因为java虚拟机的多线程是通过线程轮流切换并分配处理执行时间的方式来实现的,在任何一个确定的时刻一个处理器(特质单核)都只会执行一条线程中的指令因此为了线程切换后能恢复到正确的执行位置,每条线程都有一个程序计数器,各条线程之间互不影响,独立存储,我们称之为“线程私有”的内存如果线程正在执行的是一个java方法,这个计数器记录的则是正在执行的虚拟机字节码指令,如果是长在执行native方法则是空。这是唯一一个java虚拟机没有规定任何outofmemoryerror,java 虚拟机栈也是线程私有的他的生命周期与线程相同,虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程)自然就不会存在竞争问题。为了获得较好执行效果,java内存模型并没有限制执行引擎使用处理器的特定寄存器或缓存来和主内存进行交互,也没有限制即时编译器进行调整代码执行顺序这类优化措施。java内存模型规定了所有的变量都存储在主内存中(这里的主内存于介绍物理硬件的主内存名称一致),每条线程有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取和赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要通过主内存来完成,线程 主内存 工作内存三者交互图。这里所说的主内存 工作内存与java堆栈方法区不是一个层次的划分,这两者基本没关系,如果一定得对应起来

那从变量 主内存 工作内存之间的定义来看,主内存主要应对于java堆中对象的实例数据部分(对于hotspot来说存储对象哈希码,gc年龄,gc标志,同步锁等信息),而工作内存则对应于虚拟机栈中的部分区域,从更底层来说,主内存就直接对应于为物理硬件的内存,而为了获取更好的运行速度,虚拟机可能会让工作内存优先于寄存器和高速缓存中,因为程序运行时读写的是工作内存。



2.内存间交互

关于主内存与工作内存之间具体的交互协议,及就是一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存的实现细节java内存模型中定义了八种操作来完成,虚拟机实现必须保证下面提到的每一种操作都是原子性的,不可再分的(对于double和long类型的变量来说load store read write)操作在某些平台上允许有例外

lock 锁定 作用于主内存的变量,他把一个变量标识为一条线程独占的状态

unlock 解锁 走用与主内存的变量,他把一个处于锁定状态的变量释放出来,释放锁后的变量才可以被线程重新锁定。

read 读取 作用于主内存的变量,他把一个变量的值从主内存传输到线程的工作内存中,以便随后load 的动作使用

load 载入 作用于工作内存的变量,他把read操作从主内存中得到的变量值放入工作内存变量的副本中

use 使用 作用于工作内存的变量,他把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时会执行到这个操作。

assign 赋值 作用于工作内存的变量 他把一个从执行引擎直接接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行到这个操作

store 存储 作用作用于工作内存的变量 他把工作内存中一个变量的值传送到主内存中,一边以后的write用

write 作用于主内存的变量,他把store操作从工作内存中得到的变量的值放入主内存的变量中。

如果要把一个变量从主内存复制到工作内存,那就要顺序的执行read和load操作,如果要把工作内存一个变量复制到主内存要顺序执行store和write操作,java内存模型只要求上述两个操作必须连续。上述八种情况满足如下规则。

不允许read和load store和write操作之一单独出现,及就是不允许一个变量从主内存读取但工作内存不接受,或者从工作内存发起了回写但主内存不接受的情况出现

不允许一个线程丢弃他的最近的assign操作,就是变量在工作内存中改变了以后必须把该变化同步会主内存

不允许一个线程不允许无原因(没有任何assign操作)的把数据从线程的工作内存同步回主内存

一个新的变量只能在主内存中“诞生”不允许在工作内存直接使用一个没有被初始化(load 或assign)的变量,换句话说,就是对一个变量实施use store之前必须经多load 或assign的操作

一个变量在同一时刻只允许一条线程对其进行lock操作,但是lock操作可以被同一线程重复执行多次,多次执行后只有执行相同次数的unlock,变量才会解锁

一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量之前,需要重新执行load和assign操作初始化变量的值

对一个变量执行unlock操作之前,必须把次变量同步回主内存(执行store write)

这八种内存访问操作以及以上述规则限定,还有就是volatile一些特殊规定,就已经完全确定了java程序中哪些内存访问操作在并发下是安全的。

对volatile 特殊规则,这个在我的多线程编程中已经提到大家可以看看之前我的博客。

对long和double的特殊规则

long和double是非原子性协定。但是虚拟机基本把64位的数据读写操作作为原子操作

原子性可见性有序性

先行发生原则



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值