`
chong_zh
  • 浏览: 71957 次
  • 来自: 杭州
社区版块
存档分类
最新评论

Java内存模型的形象解释

 
阅读更多
0.什么是Java内存模型:这篇文章讨论什么不讨论什么

内存模型规定了在给定程序的条件下,某个特定的程序执行过程是否合法。内存模型只是Java运行环境与上层Java程序员之间关于内存操作语义的约定,并不规定Java内存管理的具体实现。这篇文章也只是试图用易于理解的方式描述这种约定,而不会讨论内存模型约束下内存管理机制的具体实现。


1. 定义几个基本概念
1.1 共享变量:
存储在堆内存中可以被跨线程访问的变量,包括:实例属性、类属性和数组元素。

1.2 跨线程操作
下面罗列Java内存模型所定义的所有跨线程操作:
  • 读非volatile共享变量
  • 写非volatile共享变量
  • Synchronization actions, which are:
  • (下面几项都被称作同步操作)
  • 读volatile共享变量
  • 写volatile共享变量
  • 加锁Monitor
  • 解锁Monitor
  • 某一线程的第一个和最后一个跨线程操作
  • 启动线程的操作和检测线程是否终止( T.isAlive() or T.join())的操作


线程模型只关心跨线程操作,线程内操作的对象(局部变量)与跨线程操作的对象没有交集,所以可以毫无影响的被排除在讨论范围之外。

2. 夹逼Java内存模型
2.1 编程顺序
每个线程在执行过程中,除非遇到跨线程操作,程序的语义由单线程程序执行语义决定。一旦遇到跨线程操作,跨线程操作的语义由内存模型决定,内存模型与所谓的跨线程语义等价。具体的讲即:如果线程在执行过程中遇到一个跨线程读操作,该读操作的语义由内存模型决定,并且此后线程执行过程中所读取的值都和该跨线程读操作所读到的值相等。

基于上述澄清,编程顺序可以定义为:某一线程在执行过程中,可能会发起一系列的跨线程操作。这些跨线程操作,在受到线程内语义的约束下的实际执行顺序,被称作编程顺序。

2.2 串行一致性模型
串行一致性模型是一种很强的一致性模型,定义模型最为简单,但是没有把内存模型所有的优化潜力挖掘出来。

串行一致性模型要求所有的跨线程操作都与编程顺序保持一致,准确的讲即:对于任意一个跨线程读操作,必须看到所有满足下列要求的,针对同一变量的跨线程写操作:
  • 在编程顺序中,该写操作在该读操作之前
  • 在编程顺序中,不存在任意的针对同一变量的写操作出现在该读操作之前,该写操作之后


2.3 同步顺序
1.2提出的6个同步操作引出了同步顺序:
  • 针对同一Monitor的解锁操作在同步顺序中先于其后所有的加锁操作
  • 针对同一volatile变量的写操作在同步顺序中先于其后所有的读操作
  • 启动某一线程的操作在同步顺序中先于该线程中第一个跨线程操作
  • 线程类的初始化在同步顺序中先于该线程的启动操作
  • 某一线程中的最后一个跨线程操作在同步顺序中先于检测该线程是否终止( T.isAlive() or T.join())的操作
  • 对某个线程的interrupt操作在同步顺序中先于对该线程是否interrupt检测(Thread.interrupted or Thread.isInterrupted or catch InterruptedException)的操作



2.4 Happens-before顺序
若有两个跨线程操作a1和a2,按照Happens-before顺序a1在a2之前,则记作:a1 -hb-> a2。Happens-before顺序由下面四条规则定义:
  • 同一个线程内,如果有两个跨线程操作x和y,按照编程顺序,x在y之前执行,那么x -hb-> y。
  • 如果有两个跨线程操作x和y,按照同步顺序的规则,x先于y,那么x -hb-> y。
  • 某个对象构造器的结束 -hb-> 该对象finalizer的开始
  • Happens-before顺序满足传递性,如果满足x -hb-> y,并且y -hb-> z,那么必有:x -hb-> z。


Happens-before关系是多个程序操作之间的一种特殊关系,若 x -hb-> y,并不意味着,物理的x一定在y之前执行。准确而言,x -hb-> y只保证在逻辑上,对于任意的两个观察操作o1和o2,当满足o1 -hb-> o2时,o1和o2观察到的执行顺序是x在y之前执行。


2.5 Happens-before一致性模型
Happens-before一致性模型是一种较弱的一致性模型,对一致性的要求放的较宽,只满足Happens-before一致性模型的内存管理实现可以更对的挖掘内存模型的性能优化潜力。但是只满足Happens-before一致性模型会出现上层Java程序员难以接受的结果。

在Happens-before一致性模型中,对于任意一个跨线程读操作,应该可以看到满足下列条件的,针对同一变量的所有跨线程写操作w:
  • r -hb-> w 不成立
  • 不存在针对同一变量的另一个跨线程写操作w'满足:w -hb-> w',并且w' -hb-> r。



2.6 Causality
只满足Happens-before约束的内存模型是不够强的,会产生令上层Java程序员无法接受的结果。产生这样无法接受的行为的来源是Happens-before约束没有关于数据依赖性的约束条件,在Java内存模型中被称作Causality。
下面这个例子是用来说明Causality的经典例子:
初始条件x == y == 0;
线程A线程B
r1 = x;r2 = y;
if (r1 != 0) y = 1;if (r2 != 0) x = 1;

由于Happens-before约束没有任何数据依赖性也就是Causality的约束,上述例子在编译器或者是处理器重排序优化的条件下,可能优化成下述执行序列:
  • x = 1;
  • r1 = x;
  • y = 1;
  • r2 = y;

执行结果为r1 == r2 == 1;这一结果是不符合逻辑的,上层Java程序员无法接受。

3. Java内存模型
3.1 容易理解的描述
所以综上所述,我们不难总结完整的Java内存模型的完整内容应该为:比串行一致性松,比Happens-before紧的一致性约束。具体的就是在Happens-before一致性约束的基础上再加上Causality约束,也即,Optimizer在对跨线程操作进行reorder优化的时候要与数据依赖性约束保持一致,不能破坏原有编程顺序的数据依赖性。

在上面的经典例子中,同一线程的三个操作:读,if判断,写,之间都存在着数据依赖性,所以在一个线程内的跨线程操作不能进行任何reorder优化。

3.2 严格的描述
下面两个文献包含严格的形式化描述
  • JSR 133 Java Memory Model and Thread Specification, Chapter 7
  • The Java Language Specification, Java SE 7 Edition, 17.4.6 ~ 17.4.9


3.3 补充规定
上面提出的是Java内存模型的主题规则,出此之外还有一些补充规则,用以实现对一些有用的feature和特别的平台的环境的支持。

3.3.1 final字段的语义
final字段的基本语义是该字段只能被赋值一次,一旦赋值以后无法修改,但是Java支持使用反射的方式对final字段的修改。

在Java内存模型方面,提供对final字段的freeze语义支持,具体为:
  • Java内存模型保证final字段在构造器执行退出时freeze,无论构造器是否是成功的执行完成退出。即:按照Happens-before顺序,如果对final字段的读取之后进行,则读取到final的赋值,否则为默认值。
  • Java内存模型规定,如果一个构造器调用了另一个构造器,那么,freeze在被调用的构造器退出时完成。
  • 如果对final字段使用反射进行了修改,那么freeze在修改完成之后立即完成


值得注意的是,由于final字段的取值在首次赋值之后保持不变,所以,Java可能针对final字段进行以下优化:
  • 如果final字段用了常量表达式进行赋值,那么编译器可能在出现该final字段的地方直接使用常量表达式进行替换,那么以后通过反射方式对final字段进行的修改可能不会被观察到
  • 对final字段进行的修改和读取之间不被判定为具有数据依赖性,不受Causality约束保护。


3.3.2 写保护字段
写保护字段是历史产物,System.in, System.out和System.err是static final的字段,但是可以通过System.setIn, System.setOut和System.setErr来进行修改,被区分的称作写保护字段。

写保护字段在实际处理中被当作是普通字段,但是只能被System类中的代码修改。

3.3.3 非volatile double、long和64位引用字段的操作原子性支持
在对非volatile的double、long和64位引用字段进行写时,写操作可能被拆分成两次32位的写操作。Java内存模型保证对非volatile double、long和64位引用字段的读取和写入具有原子性,永远不会读到两个不一致的32位。

3.3.4 Word Tearing
针对特殊的平台环境,略。
分享到:
评论

相关推荐

    面试技巧及其java面试题目

    - 解释Java内存模型(JMM),以及volatile关键字的作用。 - 如何避免Java中的内存泄漏? 4. 异常处理 - 什么是异常?Java中的异常处理机制是什么? - try-catch-finally语句块的执行顺序。 5. 集合框架 - ...

    oracle java认证

    8. **JVM**:对Java虚拟机的工作原理有基本认识,了解类加载机制、内存模型(堆、栈、方法区等)和性能优化策略。 9. **API使用**:熟悉Java标准库中的各种类和接口,如日期时间API、网络编程API等。 10. **注解**...

    java与c++的区别汇总[收集].pdf

    通过继承的概念,可以形象地表示Java与C++的关系:Java是从C++发展而来,继承了C++的一些特性,如面向对象,但也进行了改进和创新,比如取消了指针,引入了垃圾回收机制,以及提供了更安全的类型系统。Java的API丰富...

    JAVA医疗科技行业企业网站源码

    2. 安全性:Java提供了严格的类型检查和安全机制,有效防止了内存泄漏和缓冲区溢出等问题,特别适合处理医疗数据的安全性需求。 3. 面向对象:Java支持面向对象编程,有助于代码的组织和复用,提高软件的可维护性和...

    Java/J2EE Job Interview Companion

    如何识别Java应用程序中的性能问题和内存泄漏** - **工具选择**:使用Profiler、VisualVM等工具进行监控。 - **数据分析**:分析CPU使用率、内存消耗情况等指标。 - **问题定位**:结合日志信息定位具体问题所在...

    java面试技巧100%有效.pdf

    Java面试是检验开发者技能的关键环节,它涉及到多方面的知识,包括但不限于语法、数据结构、算法、设计模式、并发编程、JVM内存模型、框架应用、数据库操作等。以下是一些核心的Java面试知识点: 1. **基础语法**:...

    java面试题经典集合

    7. **JVM**:了解JVM的工作原理,包括类加载机制(加载、验证、准备、解析、初始化),内存模型(堆内存、栈内存、方法区、程序计数器、本地方法栈),以及调优技巧。 8. **IO流**:输入输出流的基本概念,缓冲流的...

    java自学课件

    10. **JVM与性能优化**:理解Java虚拟机的工作原理,包括内存模型、垃圾回收、类加载机制等,有助于写出更高效的代码,并进行性能调优。 这套Java自学课件不仅涵盖了以上知识点,还有可能涉及Java的高级主题,如...

    JAVA程序员面试经验.pdf,这是一份不错的文件

    3. **内存管理**:对比Java与C++在内存管理上的差异,Java采用垃圾回收机制,而C++需要程序员手动管理内存,理解垃圾回收的工作原理和内存泄漏的概念。 4. **多线程**:Java的并发编程是面试重点,理解线程的创建、...

    IO流结构图_io_java_思维导图_

    IO流是Java中的一个抽象概念,它将数据传输过程形象化为水流,分为输入流(Input Stream)和输出流(Output Stream)。输入流负责从源(如文件、网络连接等)读取数据,而输出流则负责将数据写入目标位置。 在Java...

    商业源码-编程源码-FTB v2.0(虚拟形象).zip

    FTB v2.0可能包含一套自定义虚拟形象的系统,这需要开发者理解纹理处理、3D模型导入和动画制作。 5. **用户界面(UI)**: 虚拟形象选择和定制通常涉及用户界面设计。开发者需要使用Java Swing或JavaFX等库来创建美观...

    Java .net 面试葵花宝典

    6. **JVM内存模型**:熟悉堆、栈、方法区等内存区域,垃圾回收机制(GC)以及调优策略。 7. **设计模式**:熟练掌握常见的23种设计模式,如单例、工厂、观察者等,并能在实际开发中灵活运用。 8. **Spring框架**:...

    技术人员也应懂艺术~~:不要把“老年代”叫成“Old Generation”

    在Java的内存模型中,堆内存被分为新生代(Young Generation)、老年代(Tenured Generation)和持久代(Permanent Generation,Java 8之后被元空间Metaspace取代)。新生代主要用于存放新创建的对象,经过几次垃圾...

    Java开发零基础篇:day06 面向对象(一).pdf

    文档还强调在面向对象设计中,描述对象的类(称为模型类)和测试类应当分开来编写,并且在模型类中通常不包含main方法,而是在测试类中提供main方法。 对于对象的操作,文档说明了创建对象的基本语法,即通过new...

    面试必问之jvm与性能优化.docx

    了解这些知识点,对于Java开发者来说,在面试中能够准确、深入地解释JVM和性能优化的问题,无疑会大大提升自己的专业形象。同时,理解这些机制也能帮助我们在实际开发中更好地进行性能调优,避免内存溢出、类冲突等...

    Black_Diamonds_Java

    9. **JVM和内存管理**:了解Java虚拟机(JVM)的工作原理和内存模型(堆、栈、方法区等)有助于优化代码和解决性能问题。 10. **泛型**:Java泛型允许在编译时检查类型安全,并且可以消除类型转换代码,提高代码的...

    坦克大战补充资源 无源码

    在游戏开发过程中,特别是对于经典的坦克大战游戏,素材是构建游戏场景和角色形象的基础。本压缩包提供的“坦克大战补充资源”包含了一些关键的游戏素材,这些素材可能包括: 1. **游戏背景**:通常坦克大战游戏会...

    PlaneGame.rar_游戏_Java_

    开发者可能使用这些库来设计游戏的背景、飞机模型和敌人形象。 2. **事件处理**:Java的事件监听机制使得游戏能够响应用户的键盘输入和鼠标点击,实现飞机的移动、射击和敌机的碰撞检测。 3. **多线程**:在《飞机...

    编码巧克力:每天的编码:chocolate_bar:,每天一点也不有害,对财富也有好处

    7. **JVM**:Java虚拟机是Java程序的运行环境,理解其内存模型、类加载机制和垃圾回收原理有助于优化代码性能。 8. **Spring框架**:作为Java企业级应用的主流框架,Spring涵盖了依赖注入、AOP(面向切面编程)、...

    程序员面试金典5

    此外,JVM的工作原理,包括类加载、内存模型和性能优化策略也是面试中常见的问题,书中会提供深入理解。 C++方面,书籍会涉及C++的基础语法,如指针、引用、运算符重载等。C++模板、STL(Standard Template Library...

Global site tag (gtag.js) - Google Analytics