深入理解Java虚拟机笔记---内存模型

主内存与工作内存

Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量值这样的底层细节。此处的变量(Variable)与Java编译中所说的变量略有区别,它包括了实例字段,静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不存在竞争的问题。了为获得比较好的执行效率,Java内存模型并没有限制执行引擎使用处理器的特定寄存器或缓存来和主内存进行交互,也没有限制即时编译器调整代码执行顺序这类权限。

Java内存模型规定了所有的变量都存储在主内存(Main Memory)中。每条线程还有自己的工作内存(Working Memory),线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取,赋值等)都必须是工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者的交互关系如下图:

内存间交互操作

一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存之类的实现细节,Java内存模型中定义了以下8种操作来完成。

1)lock(锁定):作用于主内存变量,它把一个变量标识为一条线程独占的状态。

2)unlock(解锁):作用于主内存变量,它把一个处理锁定的状态的变量释放出来,释放后的变量才可以被其它线程锁定。

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

4)load(载入):作用于工作内存变量,它把read操作从主内存中得到的值放入工作内存的变量副本中。

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

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

7)store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。

8)write(写入):作用于主内存的变量,它把store操作从工作内存中得到的值放入主内存的变量中。

如果要把一个变量从主内存复制到工作内存,那就要顺序地执行read和load操作,如果要把变量从工作内存同步回主内存,就要顺序地执行store和write操作。Java内存模型只是要求上述两个操作必须按顺序执行,而没有保证必须是连续执行。也就是说read与load之间、store与write之间是可以插入其它指令的,如果对主在内中的变量a,b进行访问时,一种可能出现的顺序是read a、readb、loadb、load a。除此之外,Java内存模型还规定了执行上述八种基础操作时必须满足如下规则:

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

2)不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变(为工作内存变量赋值)了之后必须把该变化同步回主内存。

3)一个新变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load和assign)的变量,换话说就是一个变量在实施use和store操作之前,必须先执行过了assign和load操作。

4)如果一个变量事先没有被load操作锁定,则不允许对它执行unlock操作:也不允许去unlock一个被其它线程锁定的变量。

5)对一个变量执行unloack之前,必须把此变量同步回主内存中(执行store和write操作)

时间: 2024-10-03 15:01:27

深入理解Java虚拟机笔记---内存模型的相关文章

深入理解Java虚拟机笔记---内存区域

Java虚拟机在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域有自各的用途,以及创建及销毁时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁.根据<Java虚拟机规范(第2版)>规定,Java虚拟机管理的内存区域包括以下几个运行时数据区域,下如图 1.程序计数器(Program Counter Register) 程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.字节码解释器工作时就是通过

深入理解Java虚拟机笔记---内存分配与回收策略

Java技术体系中的自动内存管理最终可以归结为自动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存.对象的内存分配往大的方向上讲,就是在堆上分配,对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲(-XX:+UseTLAB,默认已开启),将按线程优先在TLAB上分配.少数情况下也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数设置. 下面是几条主要的最普遍的内存分配规则: 1.对象优先在Ed

Java虚拟机:内存模型详解

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实例的时候,虚拟机要为对象分配内存,Java虚拟机又是如何配分内存的呢?这些都涉及到Java虚拟机的内存划分机制,今天我们就来探究一下Java虚拟机的内存模型. Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途以及创建和销毁的时间,有的区域随

java虚拟机之内存模型

1. 概述 对于从事 C.C++ 程序开发的人员来说,在内存管理领域,他们既是拥有最高权力的「皇帝」又是从事基础工作的「劳动人民」 --- 既拥有每个对象的「所有权」,又担负着每一个对象生命开始到终结的维护责任. 但是对于 java 程序员来说,在虚拟机自动内存管理机制的帮助下,不需要再为每一个 new 操作写配对的 delete/free 代码,不容易出现在内存泄漏和内存溢出问题,由虚拟机管理内存这一切看起来都很美好.不过,也正是因为 java 程序员把内存控制的权利交给了 java 虚拟机,

深入理解java虚拟机笔记(一)-java内存区域与内存溢出

1. 前言 这是深入理解java虚拟机一书的笔记,来自第二章.因为这本书讲的比较深奥,这是第二次看,需要记录一下笔记. 2. 运行时数据区域 java虚拟机所管理的内存分为以下几个区域. ps:图片来自网络 2.1 程序计数器 程序计数器是一块较小的内存空间,他可以看做是当前线程所执行字节码的行号指示器.字节码解释器工作时就是通过改变这个计数器的值来选去下一条要执行的字节码指令,分之.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 这块内存是线程私有的内存. 如果线程在执行

Java虚拟机的内存模型

一.问题起源 这篇文章的起源其实是博主想要学习Java并发,计算机并发的痛点在于一方面cpu越来越多核化,另外 一个方面就是cpu和存储以及通信子系统的速度差距太大,大概来说,cpu速度是ns级,内存100ns,硬盘ms,内存比cpu慢100倍, 硬盘比cpu慢100万倍,cpu如果-等这些速度慢的多的小伙伴,势必造成计算资源的浪费.物理机为了解决cpu与内存的速度差, 在两者之间加入了缓存,有缓存就会引入新问题,即缓存一致性问题. 图1(图片来自参考文献[1]) 为了解决缓存一致性问题,各个c

《深入理解Java虚拟机》 -- 内存

JVM对于操作系统来说是一种应用程序,JVM要运行的时候,操作系统会创建对应的进程而且分配一定大小的内存. 一.内存结构 当虚拟机得到系统分配的内存后,它在其内存空间中就是老大,管理对象内存的分配以及对象内存的回收,同时可以根据虚拟机的规范对其内存空间划分不同的区域.主要分为运行数据区.执行引擎.本地接口与本地类库.结构如下图: 大多数程序员更多关注的是变量是怎么存储的或者存在哪个区域.还有对象的内存分配关系,所以在这个层面上来看可以分为堆与栈两个内存区域,而这两个区域都是在运行数据区,所以我们

深入理解Java虚拟机笔记---原子性、可见性、有序性

Java内存模型是围绕着并发过程中如何处理原子性.可见性.有序性这三个特征来建立的,下面是这三个特性的实现原理: 1.原子性(Atomicity) 由Java内存模型来直接保证的原子性变量操作包括read.load.use.assign.store和write六个,大致可以认为基础数据类型的访问和读写是具备原子性的.如果应用场景需要一个更大范围的原子性保证,Java内存模型还提供了lock和unlock操作来满足这种需求,尽管虚拟机未把lock与unlock操作直接开放给用户使用,但是却提供了更

深入理解Java虚拟机--笔记1

Java内存区域与内存溢出异常 运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域. 1 程序计数器--Program Counter Register 一块较小的内存空间,作用可视为当前线程所执行的字节码的行号指示器,没个线程有自己独立的程序计数器,即"线程私有"的内存区域. 若线程当前执行的是一个Java方法,则这个计数器记录的是当前执行的虚拟机字节码指令的地址: 若线程当前执行的是一个Native方法,则这个计数器值为空(Un