Google—Java Memory Model

http://tutorials.jenkov.com/java-concurrency/java-memory-model.html

概述

The Java memory model specifies how the Java virtual machine works with the computer's memory (RAM).

  • 原始的Java内存模型不足,所以Java内存模型在Java 1.5中进行了修改。这个版本的Java内存模型仍然在Java 8中使用。

The Internal Java Memory Model

  • The Java memory model used internally in the JVM divides memory between thread stacks and the heap. (JVM内部使用的Java内存模型将线程堆栈和堆之间的内存分割。)
    该图从逻辑角度说明了Java内存模型:
从逻辑角度看Java存储器模型
  • Java虚拟机中运行的每个线程都有自己的线程堆栈。线程堆栈包含有关线程调用哪些方法来达到当前执行点的信息。我将其称为“调用堆栈”。
    当线程执行其代码时,调用堆栈发生更改。

  • 线程堆栈还包含执行的每个方法的所有局部变量(调用堆栈中的所有方法)。

  • 一个线程只能访问它自己的线程堆栈。线程所创建的局部变量对于所有其他线程,不同于创建线程的线程。即使两个线程执行完全相同的代码,两个线程仍然会在每个自己的线程堆栈中创建该代码的局部变量。因此,每个线程都有自己的每个局部变量的版本。

  • 基本类型(所有局部变量 boolean,byte,short,char,int、long, float,double)完全存储在线程栈上,因此不是其他线程可见。

  • 一个线程可以将一个pritimive变量的副本传递给另一个线程,但是它不能共享原始局部变量本身。

  • 堆包含您的Java应用程序中创建的所有对象,无论创建对象的线程如何。
    这包括原语类型(例如对象的版本Byte,Integer,Long等等)。

  • 如果对象被创建并分配给局部变量,或者创建为另一个对象的成员变量,则对象仍然存储在堆上,这并不重要。

这是一个图示出了存储在线程堆栈上的调用栈和本地变量以及存储在堆上的对象:


Java内存模型显示了本地变量和对象存储在内存中的位置。

以下是上图所示的图:

Java内存模型显示从局部变量到对象以及从对象到其他对象的引用。

Hardware Memory Architecture

现代硬件内存架构与内部Java内存模型有所不同。了解硬件内存架构也很重要,以了解Java内存模型的工作原理。本节介绍常见的硬件内存架构,后面的部分将介绍Java内存模型的工作原理。

以下是现代计算机硬件架构的简化图:

现代硬件内存架构。
  • 现代计算机通常有2个或更多的CPU。其中一些CPU也可能有多个内核。
    关键是,在具有2个或更多个CPU的现代计算机上,可以同时运行多个线程。每个CPU都可以在任何给定的时间运行一个线程。这意味着如果您的Java应用程序是多线程的,则每个CPU可能会在Java应用程序中同时(并发)运行一个线程。

  • 每个CPU都包含一组本质上是CPU内存的寄存器。CPU可以在这些寄存器上执行的操作比对主存储器中的变量执行的操作要快得多。这是因为CPU可以访问这些寄存器比访问主内存的速度快得多。

  • 每个CPU也可以具有CPU缓存存储器层。事实上,大多数现代CPU都有一些大小的缓存内存层。CPU可以比主存储器访问其高速缓存的速度快,但通常不如访问其内部寄存器一样快。因此,CPU缓存内存位于内部寄存器和主存储器的速度之间。某些CPU可能有多个缓存层(1级和2级),但是要了解Java内存模型如何与内存进行交互,这并不重要。
    重要的是知道CPU可以有某种缓存内存层。

  • 计算机还包含主存储区(RAM)。所有CPU都可以访问主存储器。主存储区通常远大于CPU的高速缓冲存储器。

  • 通常,当CPU需要访问主存储器时,它会将主存储器的一部分读入其CPU缓存。甚至可以将部分缓存读入其内部寄存器,然后对其执行操作。
    当CPU需要将结果写回主内存时,它将从内部寄存器中将值刷新到高速缓存内存,并在某些时候将值刷新回主内存。

  • 当CPU需要在高速缓冲存储器中存储其他内容时,存储在高速缓冲存储器中的值通常会刷新回主存储器。CPU缓存一次可以将数据写入其内存的一部分,并一次刷新其内存的一部分。
    每次更新时,它不必读取/写入完整的缓存。
    通常,缓存在被称为“高速缓存行”的更小的存储块中被更新。
    可以将一个或多个高速缓存行读入高速缓冲存储器,并且可以将一个或多个高速缓存线重新刷回主存储器。

Bridging The Gap Between The Java Memory Model And The Hardware Memory Architecture

  • 如前所述,Java内存模型和硬件内存架构是不同的。硬件内存架构不区分线程堆栈和堆。在硬件上,线程堆栈和堆都位于主内存中。线程堆栈和堆的一部分有时可能存在于CPU高速缓存和内部CPU寄存器中。
    这在图中说明:
CPU内部寄存器,CPU缓存和主内存之间的线程堆栈和堆的划分。
  • 当对象和变量可以存储在计算机的各种不同的存储区域中时,可能会出现某些问题。两个主要问题是:

线程更新(写入)到共享变量的可见性。
阅读,检查和写入共享变量时的竞争条件。
这两个问题将在以下部分中解释。

Visibility of Shared Objects

如果两个或多个线程共享一个对象,没有正确使用volatile声明或同步,一个线程所做的共享对象的更新对其他线程可能不可见。

假设共享对象最初存储在主内存中。在CPU上运行的线程然后将共享对象读入其CPU缓存。在那里它对共享对象进行了更改。
只要CPU缓存没有被刷新到主内存,共享对象的更改版本对于在其他CPU上运行的线程是不可见的。
这样一来,每个线程可能最终都有自己的共享对象副本,每个副本都坐在不同的CPU缓存中。

下图说明了草图的情况。
在左CPU上运行的一个线程将共享对象复制到其CPU缓存中,并将其
count变量更改为2.对于在右侧CPU上运行的其他线程,此更改不可见,因为更新count尚未刷新到主内存。

Java内存模型中的可见性问题。
要解决这个问题,可以使用Java的volatile关键字。该volatile 关键字可以确保一个给定的变量从主内存中直接读取和更新的时候总是写回主内存。

Race Conditions

如果两个或多个线程共享对象,并且多个线程更新该共享对象中的变量,则 可能会发生竞争条件

假设线程A将count共享对象的变量读入其CPU缓存中。想象一下,线程B执行相同的操作,但是进入不同的CPU缓存。
现在线程A添加一个count,线程B也一样。现在var1已经增加了两次,每次CPU缓存一次。如果这些增量依次执行,则变量count
将被递增两次,并将原始值+ 2写回主存储器。但是,两个增量是在没有正确同步的情况下同时进行的。不管线程A和B中哪一个将其更新版本写
count回到主内存,尽管有两个增量,但更新后的值将仅比原始值高1。
该图示出了如上所述的竞争条件的问题的发生:

Java内存模型中的竞争条件问题。
要解决此问题,您可以使用Java同步块

  • 同步块保证在任何给定时间只有一个线程可以进入代码的给定关键部分。
  • 同步块还保证在同步块内访问的所有变量将从主存储器读入,并且当线程退出同步块时,所有更新的变量将被再次刷新回主存储器,而不管该变量是否被声明为volatile或不。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,170评论 11 349
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,565评论 18 399
  • 好久都没有成就感了…
    果果冻冻冻阅读 199评论 0 0
  • 每个人对自己要求的生活都太相同,有人喜欢被注意,有人喜欢被忽视。 性格开朗的人喜欢一个相对活泼的环境,...
    旸蹊阅读 120评论 0 0
  • 作业一:每日晨间朋友圈见证(必须在12点之前完成) 作业二:每天朋友圈心得(当天晚上12点之前完成) 作业三:每天...
    米娜_9228阅读 180评论 0 0