java内存模型(Java Memory Model)

内容导航:

  • Java内存模型
  • 硬件存储体系结构
  • Java内存模型和硬件存储体系之间的桥梁:
  1. 共享对象的可见性
  2. 竞争条件

Java内存模型规定了JVM怎样与计算机存储系统(RAM)协调工作。JVM是一个虚拟机模型,因此这个模型自然包含一个内存的模型

理解java内存模型对于设计正确的并发程序非常重要。JVM规定了不同线程何时以及怎样能看到那些被共享变量的读写,怎样同步对共享变量的訪问控制。

最初的java内存模型并不完好。所以他在java1.5中被改动了。以下的内存模型在java1.8中仍然使用。

Java内存模型

 

JVM中内存模型被划分为栈(stack)和堆(heap)。下图是内存的逻辑图

在JVM中运行的每个线程有自己的栈空间。

每个线程栈包括有线程调用方法当前运行位置的指针。我们把它叫做栈指针。当线程运行他的代码时候,栈指针会改变。共享一块堆空间。

线程栈也包括全部正在被运行的方法的局部变量。一个线程仅仅能訪问他自己的栈。每个线程创建的局部变量对其它线程是不可见的。纵使两个线程正在运行同一段代码,他们也仅仅会创建属于自己栈空间的局部变量。也就是说每个线程有他自己的局部变量,相互之间不影响。

全部的局部基本数据类型(boolean、byte、short、char、int、long、float、double)全然存储在线程栈空间里,而且其它线程不可见。一个线程能够传递自己局部基本数据的拷贝给还有一个线程,可是他们之间并不共享。

堆空间中存放的是应用程序中全部的对象,无论是哪一个线程创建的。

而且包含基本数据对象(eg.Byte,Integer,Long等)。

无论这个对象是作为局部变量被创建,还是作为还有一个对象的成员变量被创建。他都在堆中。即一切对象都在堆空间。

看下图:

一个局部变量可能是基本数据类型。还可能是一个对象的引用。他们都在创建自己的线程栈空间,引用所指向的真正的对象在堆空间。

一个对象的成员方法。而且这些方法中也可能包括局部变量。这些局部变量在所属的线程栈中

一个对象的成员变量,无论是基本类型还是对象的引用。都存在这个堆空间的对象的内部

同一个对象能够被不同的拥有这个对象的引用的线程訪问。而且能够訪问对象的成员变量。

假设两个线程同一时刻调用同样对象的成员方法,他们都能訪问对象的成员变量,可是各自有自己的局部变量的拷贝。

以下的图阐述了上述内容:

硬件存储体系结构:

以下是简单的现代计算机的存储结构:

现代计算机通常有2个甚至很多其它的CPU,这些CPU中的一些还可能是多核的。

关键点就是多CPU的计算机同意多个线程同一时候执行(是真正的并行而非并发)。每个cpu在给定的时间片执行一个线程。假设java程序是多线程的,每个线程就能够在各个cpu上同一时候执行。

每一个cpu都包括有一系列寄存器。

cpu直接从寄存器运行运算比从内存要快非常多倍。

每个cpu还可能有cpu缓存(我们熟称的cache)。实际上大多数现代cpu都有不同容量的cache。Cpu从cache存取又比从内存快,可是比寄存器又慢一些。一些cpu还有多级缓存(L1、L2等)。可是这并不影响理解java内存模型,我们所要知道的就是在内存和cpu之间另一层缓存cache。

通常,cpu訪问主存的时候须要读一部分到cache。还可能把cache的一部分有读到寄存器,然后运行运算。当cpu须要把运算结果写回到主存的时候,会先把值从寄存器更新到cache,cache放满后再写回主存。

存储在cache中的值通常在cpu须要存储其它的东西时一起被写回到内存。

Cache把里面存储的内容一次性写回到主存。这个过程并不一定读写整个cache。通常情况下是更新cache中更小的单位,叫做cache lines 缓存行。一行或多行cache line会从主存读取到cache。或者从cache被写回主存。

Java内存模型和硬件存储体系之间的桥梁

正如已经提及的,java内存模型和硬件存储体系是不同的。

硬件存储体系并不会区分堆和栈。

在硬件层面上,堆和栈都分配在主存中。堆和栈中的一部分还可能在cache和寄存器中。例如以下图:

当对象和变量被存储在计算机不同的存储区域(rejister、cache、main-memory)的时候,就会出现故障了。

有两个问题例如以下:

  • 线程对共享变量读写的可见性
  • 读写检查共享变量的竞争条件

共享对象的可见性

假设两个或者多个线程共享一个对象而没实用volatile声明或者synchronize同步,某一个线程对共享对象的更新对其它线程可能是不可见的。

设想一下一个開始被存在主内存的共享对象。执行在cpu1上的线程把这个共享对象读入到cache。

然后对这个共享对象做更改。

仅仅要cache还没有被刷新到主存,对象的更改版本号对执行在其它cpu的线程就是不可见的。这样一来,每一个线程就使用的是自己对共享对象的拷贝,这些拷贝存储在各自cpu cache中。

以下这幅图阐述了这样的情况。

一个执行在左側cpu的线程把共享对象复制到自己的cpu cache中,而且把count值改为2.这个改变对执行在右側cpu的线程是不可见的。

由于对count的更新并没有刷新到主存。

要解决问题。我们可以使用java的volatilekeyword。Volatilekeyword可以确保被声明的变量直接从主存读取或者是直接在主存更新而不经过cache中间层。

竞争条件

多个线程对共享变量的更新还会引发竞争。

设想线程A读取共享变量的count字段到他的cache,线程B也是。如今线程A对count加1,线程B也是。

如今var1已经被添加两次,每一个cpu一次。

假设这些添加是被顺序运行(先后次序:即A read increament writeback  --> B read increament writeback)的,那么变量count将会顺序加1两次。终于结果是原始值加2写回主存。

可是,假设两次添加被并发运行(交叉次序)而没有适当的同步。那么无论是A还是B写回到主存的结果都是原始值加1。虽然做了两次加。

以下是上述问题的图:

为了解决问题,引入java的synchronized block,让某一系列操作成为原子性的,即不能够被打断(类似数据库中的事务transaction),从而实现不同线程对某一代码块的相互排斥訪问。

翻译原文地址:http://tutorials.jenkov.com/java-concurrency/java-memory-model.html

时间: 2024-12-15 07:10:39

java内存模型(Java Memory Model)的相关文章

java内存模型详解

内存模型 (memory model) 内存模型描述的是程序中各变量(实例域.静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节. 不同平台间的处理器架构将直接影响内存模型的结构. 在C或C++中, 可以利用不同操作平台下的内存模型来编写并发程序. 但是, 这带给开发人员的是, 更高的学习成本.相比之下, java利用了自身虚拟机的优势, 使内存模型不束缚于具体的处理器架构, 真正实现了跨平台.(针对hotspot jvm, jrockit等不同的

对 Java 内存模型的理解

Java 内存模型 Java内存模型规定了在多线程程序中,什么样的行为是允许出现的,什么样的行为是禁止出现的.这样说可能有点抽象,我们换一个角度.将程序行为抽象成读操作和写操作,每个线程有自己的局部变量,同时线程之间还存在共享变量.那么一个多线程程序执行结束后,所有变量会有一个最终值.Java内存模型来决定什么样的值合法,什么样的值不合法. 内存模型不能要求的太严格,这样会阻碍很多优化方法,降低程序执行的效率,但也不能要求的太松,因为这样会导致一些执行结果违反我们的直觉.例如指令间的重排序问题,

Java虚拟机12:Java内存模型

什么是Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果.在此之前,主流程序语言(如C/C++等)直接使用物理硬件和操作系统的内存模型,因此,会由于不同平台上内存模型的差异,有可能导致程序在一套平台上并发完全正常,而在另外一套平台上并发访问却经常出错,因此在某些场景下就不许针对不同的平台来编写程序. Java内存模型即要定义得足够严谨,才能

Java内存模型(JMM)

1. 概述 多任务和高并发是衡量一台计算机处理器的能力重要指标之一.一般衡量一个服务器性能的高低好坏,使用每秒事务处理数(Transactions Per Second,TPS)这个指标比较能说明问题,它代表着一秒内服务器平均能响应的请求数,而TPS值与程序的并发能力有着非常密切的关系.在讨论Java内存模型和线程之前,先简单介绍一下硬件的效率与一致性. 2.硬件的效率与一致性 由于计算机的存储设备与处理器的运算能力之间有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理

深入理解Java虚拟机- 学习笔记 - Java内存模型与线程

除了在硬件上增加告诉缓存之外,为了使得处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行(Out-Of-Order Execution)优化,处理器会在计算之后将乱序执行的结果重组,保证该结果与顺序执行的结果一致,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致,因此,如果存在一个计算任务依赖另外一个计算任务的中间结果,那么其顺序性并不能靠代码的先后顺序来保证.与处理器的乱序优化执行类似,Java虚拟机的即时编译器中也有类似的指令重排序(Instruction

011 Java内存模型与线程

1.Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果. Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节. ①主内存与工作内存 Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件时的主内存名字一样,两者也可以互相类

深入理解JMM(Java内存模型) --(一)

并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信. 同步是指程序用于控制不同线程之间操作发生相对顺序的机制.在共享内存并发

深入理解JVM读书笔记五: Java内存模型与Volatile关键字

12.2硬件的效率与一致性 由于计算机的存储设备与处理器的运算速度有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中,这样处理器就无须等待缓慢的内存读写了. 基于高速缓存的存储交互很好地理解了处理器与内存的速度矛盾,但是也为计算机系统带来了更高的复杂度,因为它引入了一个新的问题: 缓存一致性(Cache Coherenc

全面理解Java内存模型

尊重原创:http://blog.csdn.net/suifeng3051/article/details/52611310 Java内存模型即JavaMemory Model,简称JMM.JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式.JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的. 如果我们要想深入了解Java并发编程,就要先理解好Java内存模型.Java内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步.原始的Java内存模型

由volatile关键字谈Java内存模型

volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用volatile关键字的场景 1. 内存模型的相关概念 当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再