Java内存模型

466 阅读5分钟

Java内存模型

1. 前言

Java 内存模型是根据英文Java Memory Model(JMM)翻译过来的。其实JMM并不像JVM内存结构一样是真实存在的。他只是一个抽象的概念。用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的并发效果。

在 Java 面试中,一般会问 Java 内存模型,本篇将揭开 Java 内存模型神秘的面纱。

2. Java 内存模型的抽象结构

并发编程要解决的关键问题:

  1. 线程之间如何进行通信
  2. 线程之间如何进行同步

通信是指线程之间以何种机制进行交换信息。在命令行编程中,线程之间的通信机制有两种:共享内存和消息传递 。在共享内存模型中,线程之间共享程序的公共状态,通过写 - 读内存中的公共状态进行隐式通信。在消息传递的并发模型中,线程之间没有公共状态,线程之间必须通过发送消息显式进行通信。

同步是指程序中控制不同线程间操作发生相对顺序的机制。在共享内存并发模型里,同步时显式进行的。

Java 的并发采用的是共享内存模型,Java 线程之间的通信总是隐式进行,整个通信过程是完全透明的。

Java 把内存分为两个部分:

  • 主内存:储存实例域、静态域、数组元素
  • 工作内存:储存局部变量、方法定义参数、异常处理器参数

JMM 抽象结构示意图

Java 内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。

而 JMM 就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。

3. Java 内存模型的实现

在Java中提供了一系列和并发处理相关的关键字,比如 volatilesynchronizedfinalconcurren 包等。其实这些就是Java内存模型封装了底层的实现后提供给程序员使用的一些关键字。

在开发多线程的代码的时候,我们可以直接使用 synchronized 等关键字来控制并发,从来就不需要关心底层的编译器优化、缓存一致性等问题。所以,Java 内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。

4. JVM 内存结构

面试中,面试官问我Java内存模型,我说成了 JVM 内存结构,很多人都把这概念搞混了,其实完全不一样,所以这里也讲讲 JVM 内存结构。

《Java虚拟机规范(Java SE 8)》中描述了JVM运行时内存区域结构如下:

JVM 内存结构

  1. 以上是Java虚拟机规范,不同的虚拟机实现会各有不同,但是一般会遵守规范

  2. 规范中定义的方法区,只是一种概念上的区域,并说明了其应该具有什么功能。但是并没有规定这个区域到底应该处于何处。所以,对于不同的虚拟机实现来说,是由一定的自由度的。

  3. 不同版本的方法区所处位置不同,上图中划分的是逻辑区域,并不是绝对意义上的物理区域。因为某些版本的JDK中方法区其实是在堆中实现的

  4. 运行时常量池用于存放编译期生成的各种字面量和符号应用。但是,Java语言并不要求常量只有在编译期才能产生。比如在运行期,String.intern也会把新的常量放入池中。

  5. 除了以上介绍的JVM运行时内存外,还有一块内存区域可供使用,那就是直接内存。Java虚拟机规范并没有定义这块内存区域,所以他并不由JVM管理,是利用本地方法库直接在堆外申请的内存区域。

  6. 堆和栈的数据划分也不是绝对的,如HotSpot的JIT会针对对象分配做相应的优化。

详细的对比可以参考:JVM内存结构 VS Java内存模型 VS Java对象模型

5. 小结&参考资料

小结

JMM 是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。对于 JMM 设计的关键字,后续文章会有部分讲解。

参考资料